Checking GIMP's Source Code with PVS-Studio 
Author: Andrey Karpov 
Date: 15.08.2014 
To check GIMP, we should first find a way to get it compile successfully. This task is far from easy, that's 
why we had been constantly delaying the check. However, the project is too famous, and we were very 
interested to find out its quality. So we have conquered our laziness and completed the analysis. 
GIMP 
I don't like GIMP's interface, though I do use this graphics editor from time to time. It doesn't make 
sense to purchase Photoshop only for editing the image of our unicorn for another article a few times in 
a month; Paint and GIMP will do quite well. 
I can't call myself a user experienced enough to reasonably judge about convenience. But you don't 
have to be a carpenter or a furniture expert to tell when nails sticking up from a chair make it 
uncomfortable to sit on. So I can point out a few defects in GIMP that bother me when working with it. 
For instance, when opening a file, you cannot paste a complete file path in the Location field if the path 
contains Russian letters. And there are quite a lot of other similar defects. 
Too well familiar with the clumsy GIMP's interface, I expected to find a bunch of bugs in the code. But I 
was wrong. The project developers appear to have been using static analysis for some time already. And 
what they use is heavy artillery – one of the most powerful static analyzers, Coverity. 
It was mentioned on the Internet: 
The Coverity project established with the support of the USA government and focusing on detecting 
programming errors in open source programs, announces that 100 open source graphics software 
projects will be included in their SCAN project for source code analysis among which are Scribus, GIMP, 
Inkscape, Krita, Blender, and many others (from a 2007 publication). 
Analysis results 
Let's see if we can find anything of interest in GIMP's code after it has been cleaned out by Coverity. 
Analysis was done by PVS-Studio 5.18. 
Fragments No. 1 – No. 3 
typedef double gdouble;
GimpBlob * 
gimp_blob_square (gdouble xc, 
gdouble yc, 
gdouble xp, 
gdouble yp, 
gdouble xq, 
gdouble yq) 
{ 
GimpBlobPoint points[4]; 
/* Make sure we order points ccw */ 
if (xp * yq - yq * xp < 0) 
{ 
xq = -xq; 
yq = -yq; 
} 
.... 
} 
PVS-Studio's diagnostic message: V501 There are identical sub-expressions to the left and to the right of 
the '-' operator: xp * yq - yq * xp gimpink-blob.c 162 
The "xp * yq - yq * xp" expression is very strange. The value "xp*yq" is subtracted from itself. 
Similar checks can be found a bit further in this file. Look for lines 195 and 278. 
Fragment No. 4 
gint64 gimp_g_value_get_memsize (GValue *value) 
{ 
.... 
GimpArray *array = g_value_get_boxed (value); 
if (array) 
memsize += (sizeof (GimpArray) + 
array->static_data ? 0 : array->length); 
....
} 
PVS-Studio's diagnostic message: V502 Perhaps the '?:' operator works in a different way than it was 
expected. The '?:' operator has a lower priority than the '+' operator. gimp-utils.c 233 
There is a mess in operator precedence. 0 or "array->length" must be added to the size of some object. 
But the '+' operator's priority is higher than that of '?:'. The expression therefore will execute in the 
following way: 
memsize += ((sizeof (GimpArray) + array->static_data) ? 
0 : array->length); 
The programmer seems to have known about it, that's why he used parentheses. But then one of them 
is in a wrong place. The correct code should look as follows: 
memsize += sizeof (GimpArray) + 
(array->static_data ? 0 : array->length); 
Fragments No. 5, No. 6 
#define cmsFLAGS_NOOPTIMIZE 0x0100 
#define cmsFLAGS_BLACKPOINTCOMPENSATION 0x2000 
static void 
lcms_layers_transform_rgb (...., gboolean bpc) 
{ 
.... 
transform = cmsCreateTransform ( 
src_profile, lcms_format, 
dest_profile, lcms_format, 
intent, 
cmsFLAGS_NOOPTIMIZE | 
bpc ? cmsFLAGS_BLACKPOINTCOMPENSATION : 0); 
.... 
} 
PVS-Studio's diagnostic message: V502 Perhaps the '?:' operator works in a different way than it was 
expected. The '?:' operator has a lower priority than the '|' operator. lcms.c 1016 
Depending on the 'bpc' variable, the function should receive either the 
"cmsFLAGS_BLACKPOINTCOMPENSATION" flag or a combination of flags 
"cmsFLAGS_BLACKPOINTCOMPENSATION | cmsFLAGS_NOOPTIMIZE".
The '|' operator's priority is higher than that of the ternary operator '?:'. As a result, the '?:' operator has 
the "cmsFLAGS_NOOPTIMIZE | bpc" expression as its condition. And this condition is always true. The 
function always receives the cmsFLAGS_BLACKPOINTCOMPENSATION flag. 
The correct code should look like this: 
transform = cmsCreateTransform ( 
src_profile, lcms_format, 
dest_profile, lcms_format, 
intent, 
cmsFLAGS_NOOPTIMIZE | 
(bpc ? cmsFLAGS_BLACKPOINTCOMPENSATION : 0)); 
The same error can be found in lcms.c 1016. 
Fragment No. 7 
static gint load_resource_lrfx (....) 
{ 
.... 
else if (memcmp (effectname, "oglw", 4) == 0) <<<=== 
.... 
else if (memcmp (effectname, "iglw", 4) == 0) 
.... 
else if (memcmp (effectname, "oglw", 4) == 0) <<<=== 
.... 
else if (memcmp (effectname, "bevl", 4) == 0) 
.... 
} 
PVS-Studio's diagnostic message: V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There 
is a probability of logical error presence. Check lines: 602, 688. psd-layer-res-load.c 602 
Two identical conditions in the if-elseif-elseif-... sequence. 
Fragment No. 8 
void 
gimp_text_get_transformation (GimpText *text, 
GimpMatrix3 *matrix)
{ 
g_return_if_fail (GIMP_IS_TEXT (text)); 
g_return_if_fail (matrix != NULL); 
matrix->coeff[0][0] = text->transformation.coeff[0][0]; 
matrix->coeff[0][1] = text->transformation.coeff[0][1]; 
matrix->coeff[0][2] = text->offset_x; 
matrix->coeff[1][0] = text->transformation.coeff[1][0]; 
matrix->coeff[1][1] = text->transformation.coeff[1][1]; 
matrix->coeff[1][2] = text->offset_y; 
matrix->coeff[2][0] = 0.0; 
matrix->coeff[2][1] = 0.0; 
matrix->coeff[2][1] = 1.0; <<<=== 
} 
PVS-Studio's diagnostic message: V519 The 'matrix->coeff[2][1]' variable is assigned values twice 
successively. Perhaps this is a mistake. Check lines: 567, 568. gimptext.c 568 
The Last Line Effect. In the very end, an incorrect index is used. It should be like this: 
matrix->coeff[2][0] = 0.0; 
matrix->coeff[2][1] = 0.0; 
matrix->coeff[2][2] = 1.0; 
Fragment No. 9 
static void warp_one (....) 
{ 
.... 
if (first_time) 
gimp_pixel_rgn_init (&dest_rgn, 
new, x1, y1, (x2 - x1), (y2 - y1), 
TRUE, TRUE); 
else 
gimp_pixel_rgn_init (&dest_rgn, 
new, x1, y1, (x2 - x1), (y2 - y1),
TRUE, TRUE); 
.... 
} 
PVS-Studio's diagnostic message: V523 The 'then' statement is equivalent to the 'else' statement. warp.c 
1366 
It is very suspicious that one and the same branch is executed regardless of the condition. 
Fragments No. 10, No. 11, No. 12 
gboolean gimp_wire_read (GIOChannel *channel, 
guint8 *buf, 
gsize count, 
gpointer user_data) 
{ 
g_return_val_if_fail (count >= 0, FALSE); 
.... 
} 
PVS-Studio's diagnostic message: V547 Expression 'count >= 0' is always true. Unsigned type value is 
always >= 0. gimpwire.c 99 
The "count >= 0" check doesn't make sense as the 'count' variable is unsigned. Perhaps it's not a serious 
bug but I still should mention it. 
Similar checks: gimpwire.c 170; gimpcageconfig.c 428. 
Below we will discuss more interesting issues found through the V547 diagnostic. 
Fragment No. 13 
static GimpPlugInImageType 
image_types_parse (const gchar *name, 
const gchar *image_types) 
{ 
.... 
while (*image_types && 
((*image_types != ' ') || 
(*image_types != 't') || 
(*image_types != ','))) 
{
image_types++; 
} 
.... 
} 
PVS-Studio's diagnostic message: V547 Expression is always true. Probably the '&&' operator should be 
used here. gimppluginprocedure.c 808 
To make it clearer, I've made an artificial example: 
int A = ...; 
if ( A != 1 || A != 2 || A != 3) 
Regardless of the value the A variable takes, the condition is always true. 
Fragment No. 14 
static gunichar basic_inchar(port *pt) { 
.... 
gunichar c; 
.... 
c = g_utf8_get_char_validated(pt->rep.string.curr, len); 
if (c >= 0) /* Valid UTF-8 character? */ 
{ 
len = g_unichar_to_utf8(c, NULL); 
pt->rep.string.curr += len; 
return c; 
} 
/* Look for next valid UTF-8 character in buffer */ 
pt->rep.string.curr = g_utf8_find_next_char( 
pt->rep.string.curr, 
pt->rep.string.past_the_end); 
.... 
} 
PVS-Studio's diagnostic message: V547 Expression 'c >= 0' is always true. Unsigned type value is always 
>= 0. scheme.c 1654 
All the characters will be treated as correct UTF-8 characters. The 'c' variable is unsigned, so the (c >= 0) 
condition is always true.
Fragment No. 15 
#define ABS(a) (((a) < 0) ? -(a) : (a)) 
static gint32 
load_thumbnail (...., gint32 thumb_size, ....) 
{ 
.... 
guint32 size; 
guint32 diff; 
.... 
diff = ABS(thumb_size - size); 
.... 
} 
PVS-Studio's diagnostic message: V547 Expression '(thumb_size - size) < 0' is always false. Unsigned type 
value is never < 0. file-xmc.c 874 
The program works differently than the programmer expected. Suppose the 'thumb_size' variable 
equals 10 and the 'size' variable equals 25. 
It may seem at first that the expression will evaluate to 15. But actually the result will be 0xFFFFFFF1 
(4294967281). 
The "thumb_size - size" expression is unsigned. As a result, we'll get number 0xFFFFFFF1u. The ABS 
macro doesn't do anything in this case. 
Fragment No. 16 
static gchar * 
script_fu_menu_map (const gchar *menu_path) 
{ 
.... 
const gchar *suffix = menu_path + strlen (mapping[i].old); 
if (! *suffix == '/') 
continue; 
.... 
} 
PVS-Studio's diagnostic message: V562 It's odd to compare 0 or 1 with a value of 47: !* suffix == '/'. 
script-fu-scripts.c 859
Another trouble with operator precedence. First, the "!*suffix" expression is calculated. Its result is 
either 0 or 1. This number is then compared to the '/' character, which doesn't make any sense at all. 
The correct code: 
if (*suffix != '/') 
Fragment No. 17 
static void 
save_file_chooser_response (GtkFileChooser *chooser, 
gint response_id, 
GFigObj *obj) 
{ 
.... 
gfig_context->current_obj = obj; 
gfig_save_callbk (); 
gfig_context->current_obj = gfig_context->current_obj; 
.... 
} 
PVS-Studio's diagnostic message: V570 The 'gfig_context->current_obj' variable is assigned to itself. gfig-dialog. 
c 1623 
The variable is copied into itself. 
Fragment No. 18 
size g_strlcpy(gchar *dest, const gchar *src, gsize dest_size); 
GList * gimp_brush_generated_load (....) 
{ 
.... 
gchar *string; 
.... 
/* the empty string is not an allowed name */ 
if (strlen (string) < 1) 
g_strlcpy (string, _("Untitled"), sizeof (string)); 
.... 
}
PVS-Studio's diagnostic message: V579 The g_strlcpy function receives the pointer and its size as 
arguments. It is possibly a mistake. Inspect the third argument. gimpbrushgenerated-load.c 119 
The "sizeof(string)" operator calculates the pointer size, not the buffer size. 
Fragment No. 19 
static gboolean save_image (....) 
{ 
.... 
gint c; 
.... 
if (has_alpha && (data[rowoffset + k + 1] < 128)) 
c |= 0 << (thisbit ++); 
else 
.... 
} 
PVS-Studio's diagnostic message: V684 A value of the variable 'c' is not modified. Consider inspecting the 
expression. It is possible that '1' should be present instead of '0'. file-xbm.c 1136 
The "c |= 0 << (thisbit ++);" expression doesn't change the 'c' variable. 
I've noticed that code like that is very likely to be found when the programmer wanted to zero out a 
certain bit but made a mistake. Then the code should look as follows: 
c &= ~(1u << (thisbit ++)); 
Fragment No. 20 
gboolean gimp_item_get_popup_size (...., 
gint *popup_width, gint *popup_height) 
{ 
.... 
if (scaling_up) 
{ 
*popup_width = gimp_item_get_width (item); 
*popup_width = gimp_item_get_height (item); 
} 
.... 
}
PVS-Studio's diagnostic message: V537 Consider reviewing the correctness of 'popup_width' item's 
usage. gimpitem-preview.c 126 
This is a typo or a consequence of the Copy-Paste technique. The correct code: 
*popup_width = gimp_item_get_width (item); 
*popup_height = gimp_item_get_height (item); 
Fragment No. 21 
gboolean gimp_draw_tool_on_vectors_curve (...., 
GimpAnchor **ret_segment_start, 
GimpAnchor **ret_segment_end, 
....) 
{ 
.... 
if (ret_segment_start) *ret_segment_start = NULL; 
if (ret_segment_start) *ret_segment_end = NULL; 
.... 
} 
PVS-Studio's diagnostic message: V581 The conditional expressions of the 'if' operators situated 
alongside each other are identical. Check lines: 1212, 1213. gimpdrawtool.c 1213 
This is a typo or a consequence of the Copy-Paste technique. The correct code: 
if (ret_segment_start) *ret_segment_start = NULL; 
if (ret_segment_end) *ret_segment_end = NULL; 
Fragments No. 22 – No. 40 
ObjectList_t* 
object_list_append_list(ObjectList_t *des, ObjectList_t *src) 
{ 
GList *p; 
for (p = src->list; p; p = p->next) 
object_list_append(des, object_clone((Object_t*) p->data)); 
object_list_set_changed(des, (src) ? TRUE : FALSE); 
return des; 
}
PVS-Studio's diagnostic message: V595 The 'src' pointer was utilized before it was verified against 
nullptr. Check lines: 536, 538. imap_object.c 536 
You may conclude from the "(src) ? TRUE : FALSE" condition that the 'src' pointer may be equal to 
nullptr. 
However, this pointer is bravely dereferenced in the "p = src->list" expression a bit earlier, which is an 
error. 
There are other fragments that triggered the V595 warning as well. They also need checking: 
• The 'l' pointer. Check lines: 262, 265. gimpimage-item-list.c 262 
• The 'quantobj' pointer. Check lines: 965, 971. gimpimage-convert-type.c 965 
• The 'slist' pointer. Check lines: 683, 685. gimpfont.c 683 
• The 'dock_window->p->context' pointer. Check lines: 487, 504. gimpdockwindow.c 487 
• The 'layer_renderer' pointer. Check lines: 1245, 1275. gimplayertreeview.c 1245 
• The 'shell->display' pointer. Check lines: 574, 588. gimpdisplayshell-dnd.c 574 
• The 'ops' pointer. Check lines: 265, 267. gimpgegltool.c 265 
• The 'dialog' pointer. Check lines: 234, 249. file-save-dialog.c 234 
• The 'shell' pointer. Check lines: 738, 763. view-actions.c 738 
• The 'fname' pointer. Check lines: 1426, 1437. scheme.c 1426 
• The 'sgip->table' pointer. Check lines: 148, 161. sgi-lib.c 148 
• The 'sgip->length' pointer. Check lines: 154, 167. sgi-lib.c 154 
• The 'pixels' pointer. Check lines: 1482, 1508. psd-load.c 1482 
• The 'img_a->alpha_names' pointer. Check lines: 1735, 1741. psd-load.c 1735 
• The 'brush' pointer. Check lines: 432, 451. brush.c 432 
• The 'curve_list->data' pointer. Check lines: 126, 129. curve.c 126 
• The 'outline_list->data' pointer. Check lines: 183, 187. pxl-outline.c 183 
• The 'id_ptr' pointer. Check lines: 896, 898. sample-colorize.c 896 
Conclusion 
It's not easy to tell how critical the bugs found in this project are. But I will be pleased if some of them 
will be fixed thanks to this article. 
Although I told you in the beginning that I don't like GIMP's interface, I'm still thankful to its authors for 
their project. Quite a number of images for my articles were made in GIMP. Thank you.

More Related Content

PDF
Tesseract. Recognizing Errors in Recognition Software
PDF
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
PDF
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
PDF
PVS-Studio vs Chromium - Continuation
PDF
Reanalyzing the Notepad++ project
PDF
A Spin-off: CryEngine 3 SDK Checked with CppCat
PDF
I want to sell a PVS-Studio license to the Intel company
PDF
Checking WinMerge with PVS-Studio for the second time
Tesseract. Recognizing Errors in Recognition Software
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
PVS-Studio vs Chromium - Continuation
Reanalyzing the Notepad++ project
A Spin-off: CryEngine 3 SDK Checked with CppCat
I want to sell a PVS-Studio license to the Intel company
Checking WinMerge with PVS-Studio for the second time

What's hot (20)

PDF
Checking the World of Warcraft CMaNGOS open source server
PDF
Checking Notepad++: five years later
PDF
Checking the code of Valgrind dynamic analyzer by a static analyzer
PDF
A Slipshod Check of the Visual C++ 2013 Library (update 3)
PDF
Picking Mushrooms after Cppcheck
PDF
CppCat Checks OpenMW: Not All is Fine in the Morrowind Universe
PDF
The Unicorn's Travel to the Microcosm
PDF
Analyzing the Dolphin-emu project
PDF
The CppCat Analyzer Checks TortoiseGit
PDF
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
PDF
Anomalies in X-Ray Engine
PDF
Intel IPP Samples for Windows - error correction
PDF
Top 10 bugs in C++ open source projects, checked in 2016
PDF
PVS-Studio for Linux Went on a Tour Around Disney
PDF
Mathematicians: Trust, but Verify
PDF
Checking the Cross-Platform Framework Cocos2d-x
PDF
Checking VirtualDub
PDF
Errors that static code analysis does not find because it is not used
PDF
Sony C#/.NET component set analysis
PDF
Pre New Year Check of PostgreSQL
Checking the World of Warcraft CMaNGOS open source server
Checking Notepad++: five years later
Checking the code of Valgrind dynamic analyzer by a static analyzer
A Slipshod Check of the Visual C++ 2013 Library (update 3)
Picking Mushrooms after Cppcheck
CppCat Checks OpenMW: Not All is Fine in the Morrowind Universe
The Unicorn's Travel to the Microcosm
Analyzing the Dolphin-emu project
The CppCat Analyzer Checks TortoiseGit
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
Anomalies in X-Ray Engine
Intel IPP Samples for Windows - error correction
Top 10 bugs in C++ open source projects, checked in 2016
PVS-Studio for Linux Went on a Tour Around Disney
Mathematicians: Trust, but Verify
Checking the Cross-Platform Framework Cocos2d-x
Checking VirtualDub
Errors that static code analysis does not find because it is not used
Sony C#/.NET component set analysis
Pre New Year Check of PostgreSQL
Ad

Viewers also liked (15)

PPTX
PVS-Studio and static code analysis technique
PDF
Archeology for Entertainment, or Checking Microsoft Word 1.1a with PVS-Studio
PDF
Wade Not in Unknown Waters. Part Four.
PDF
Trying to Sell PVS-Studio to Google, or New Bugs in Chromium
PDF
PVS-Studio's New Message Suppression Mechanism
PDF
Three Interviews About Static Code Analyzers
PDF
The Last Line Effect
PDF
Static analysis is most efficient when being used regularly. We'll tell you w...
PDF
A Post About Analyzing PHP
PDF
Checking Oracle VM VirtualBox. Part 2
PDF
Static and Dynamic Code Analysis
PDF
CppCat, an Ambitious C++ Code Analyzer from Tula
PDF
Checking the Source SDK Project
PDF
Checking the Qt 5 Framework
PDF
Handling False Positives in PVS-Studio and CppCat
PVS-Studio and static code analysis technique
Archeology for Entertainment, or Checking Microsoft Word 1.1a with PVS-Studio
Wade Not in Unknown Waters. Part Four.
Trying to Sell PVS-Studio to Google, or New Bugs in Chromium
PVS-Studio's New Message Suppression Mechanism
Three Interviews About Static Code Analyzers
The Last Line Effect
Static analysis is most efficient when being used regularly. We'll tell you w...
A Post About Analyzing PHP
Checking Oracle VM VirtualBox. Part 2
Static and Dynamic Code Analysis
CppCat, an Ambitious C++ Code Analyzer from Tula
Checking the Source SDK Project
Checking the Qt 5 Framework
Handling False Positives in PVS-Studio and CppCat
Ad

Similar to Checking GIMP's Source Code with PVS-Studio (20)

PDF
Analyzing the Blender project with PVS-Studio
PDF
Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...
PDF
Re-checking the ReactOS project - a large report
PDF
Checking Wine with PVS-Studio and Clang Static Analyzer
PDF
Errors detected in C++Builder
PDF
Analysis of Godot Engine's Source Code
PDF
100 bugs in Open Source C/C++ projects
PDF
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
PDF
Can We Trust the Libraries We Use?
PDF
LibRaw, Coverity SCAN, PVS-Studio
PDF
Critical errors in CryEngine V code
PDF
Waiting for the Linux-version: Checking the Code of Inkscape Graphics Editor
PDF
PVS-Studio delved into the FreeBSD kernel
PDF
Explanations to the article on Copy-Paste
PDF
Checking OpenCV with PVS-Studio
PDF
Spring RTS Engine Checkup
PDF
A fresh eye on Oracle VM VirtualBox
PDF
Analyzing FreeCAD's Source Code and Its "Sick" Dependencies
PDF
Firefox Easily Analyzed by PVS-Studio Standalone
PDF
Bugs found in GCC with the help of PVS-Studio
Analyzing the Blender project with PVS-Studio
Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...
Re-checking the ReactOS project - a large report
Checking Wine with PVS-Studio and Clang Static Analyzer
Errors detected in C++Builder
Analysis of Godot Engine's Source Code
100 bugs in Open Source C/C++ projects
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
Can We Trust the Libraries We Use?
LibRaw, Coverity SCAN, PVS-Studio
Critical errors in CryEngine V code
Waiting for the Linux-version: Checking the Code of Inkscape Graphics Editor
PVS-Studio delved into the FreeBSD kernel
Explanations to the article on Copy-Paste
Checking OpenCV with PVS-Studio
Spring RTS Engine Checkup
A fresh eye on Oracle VM VirtualBox
Analyzing FreeCAD's Source Code and Its "Sick" Dependencies
Firefox Easily Analyzed by PVS-Studio Standalone
Bugs found in GCC with the help of PVS-Studio

More from Andrey Karpov (20)

PDF
60 антипаттернов для С++ программиста
PDF
60 terrible tips for a C++ developer
PPTX
Ошибки, которые сложно заметить на code review, но которые находятся статичес...
PDF
PVS-Studio in 2021 - Error Examples
PDF
PVS-Studio in 2021 - Feature Overview
PDF
PVS-Studio в 2021 - Примеры ошибок
PDF
PVS-Studio в 2021
PPTX
Make Your and Other Programmer’s Life Easier with Static Analysis (Unreal Eng...
PPTX
Best Bugs from Games: Fellow Programmers' Mistakes
PPTX
Does static analysis need machine learning?
PPTX
Typical errors in code on the example of C++, C#, and Java
PPTX
How to Fix Hundreds of Bugs in Legacy Code and Not Die (Unreal Engine 4)
PPTX
Game Engine Code Quality: Is Everything Really That Bad?
PPTX
C++ Code as Seen by a Hypercritical Reviewer
PPTX
The Use of Static Code Analysis When Teaching or Developing Open-Source Software
PPTX
Static Code Analysis for Projects, Built on Unreal Engine
PPTX
Safety on the Max: How to Write Reliable C/C++ Code for Embedded Systems
PPTX
The Great and Mighty C++
PPTX
Static code analysis: what? how? why?
PDF
Zero, one, two, Freddy's coming for you
60 антипаттернов для С++ программиста
60 terrible tips for a C++ developer
Ошибки, которые сложно заметить на code review, но которые находятся статичес...
PVS-Studio in 2021 - Error Examples
PVS-Studio in 2021 - Feature Overview
PVS-Studio в 2021 - Примеры ошибок
PVS-Studio в 2021
Make Your and Other Programmer’s Life Easier with Static Analysis (Unreal Eng...
Best Bugs from Games: Fellow Programmers' Mistakes
Does static analysis need machine learning?
Typical errors in code on the example of C++, C#, and Java
How to Fix Hundreds of Bugs in Legacy Code and Not Die (Unreal Engine 4)
Game Engine Code Quality: Is Everything Really That Bad?
C++ Code as Seen by a Hypercritical Reviewer
The Use of Static Code Analysis When Teaching or Developing Open-Source Software
Static Code Analysis for Projects, Built on Unreal Engine
Safety on the Max: How to Write Reliable C/C++ Code for Embedded Systems
The Great and Mighty C++
Static code analysis: what? how? why?
Zero, one, two, Freddy's coming for you

Recently uploaded (20)

PPTX
Download Adobe Photoshop Crack 2025 Free
DOCX
How to Use SharePoint as an ISO-Compliant Document Management System
PDF
DuckDuckGo Private Browser Premium APK for Android Crack Latest 2025
PDF
AI/ML Infra Meetup | Beyond S3's Basics: Architecting for AI-Native Data Access
PDF
AI-Powered Threat Modeling: The Future of Cybersecurity by Arun Kumar Elengov...
PPTX
MLforCyber_MLDataSetsandFeatures_Presentation.pptx
PDF
Top 10 Software Development Trends to Watch in 2025 🚀.pdf
PDF
novaPDF Pro 11.9.482 Crack + License Key [Latest 2025]
PDF
Multiverse AI Review 2025: Access All TOP AI Model-Versions!
PPTX
WiFi Honeypot Detecscfddssdffsedfseztor.pptx
PDF
BoxLang Dynamic AWS Lambda - Japan Edition
PDF
AI Guide for Business Growth - Arna Softech
PDF
Visual explanation of Dijkstra's Algorithm using Python
PDF
Guide to Food Delivery App Development.pdf
PPTX
4Seller: The All-in-One Multi-Channel E-Commerce Management Platform for Glob...
PDF
Ableton Live Suite for MacOS Crack Full Download (Latest 2025)
PDF
Introduction to Ragic - #1 No Code Tool For Digitalizing Your Business Proces...
PDF
DNT Brochure 2025 – ISV Solutions @ D365
PDF
CCleaner 6.39.11548 Crack 2025 License Key
PDF
MCP Security Tutorial - Beginner to Advanced
Download Adobe Photoshop Crack 2025 Free
How to Use SharePoint as an ISO-Compliant Document Management System
DuckDuckGo Private Browser Premium APK for Android Crack Latest 2025
AI/ML Infra Meetup | Beyond S3's Basics: Architecting for AI-Native Data Access
AI-Powered Threat Modeling: The Future of Cybersecurity by Arun Kumar Elengov...
MLforCyber_MLDataSetsandFeatures_Presentation.pptx
Top 10 Software Development Trends to Watch in 2025 🚀.pdf
novaPDF Pro 11.9.482 Crack + License Key [Latest 2025]
Multiverse AI Review 2025: Access All TOP AI Model-Versions!
WiFi Honeypot Detecscfddssdffsedfseztor.pptx
BoxLang Dynamic AWS Lambda - Japan Edition
AI Guide for Business Growth - Arna Softech
Visual explanation of Dijkstra's Algorithm using Python
Guide to Food Delivery App Development.pdf
4Seller: The All-in-One Multi-Channel E-Commerce Management Platform for Glob...
Ableton Live Suite for MacOS Crack Full Download (Latest 2025)
Introduction to Ragic - #1 No Code Tool For Digitalizing Your Business Proces...
DNT Brochure 2025 – ISV Solutions @ D365
CCleaner 6.39.11548 Crack 2025 License Key
MCP Security Tutorial - Beginner to Advanced

Checking GIMP's Source Code with PVS-Studio

  • 1. Checking GIMP's Source Code with PVS-Studio Author: Andrey Karpov Date: 15.08.2014 To check GIMP, we should first find a way to get it compile successfully. This task is far from easy, that's why we had been constantly delaying the check. However, the project is too famous, and we were very interested to find out its quality. So we have conquered our laziness and completed the analysis. GIMP I don't like GIMP's interface, though I do use this graphics editor from time to time. It doesn't make sense to purchase Photoshop only for editing the image of our unicorn for another article a few times in a month; Paint and GIMP will do quite well. I can't call myself a user experienced enough to reasonably judge about convenience. But you don't have to be a carpenter or a furniture expert to tell when nails sticking up from a chair make it uncomfortable to sit on. So I can point out a few defects in GIMP that bother me when working with it. For instance, when opening a file, you cannot paste a complete file path in the Location field if the path contains Russian letters. And there are quite a lot of other similar defects. Too well familiar with the clumsy GIMP's interface, I expected to find a bunch of bugs in the code. But I was wrong. The project developers appear to have been using static analysis for some time already. And what they use is heavy artillery – one of the most powerful static analyzers, Coverity. It was mentioned on the Internet: The Coverity project established with the support of the USA government and focusing on detecting programming errors in open source programs, announces that 100 open source graphics software projects will be included in their SCAN project for source code analysis among which are Scribus, GIMP, Inkscape, Krita, Blender, and many others (from a 2007 publication). Analysis results Let's see if we can find anything of interest in GIMP's code after it has been cleaned out by Coverity. Analysis was done by PVS-Studio 5.18. Fragments No. 1 – No. 3 typedef double gdouble;
  • 2. GimpBlob * gimp_blob_square (gdouble xc, gdouble yc, gdouble xp, gdouble yp, gdouble xq, gdouble yq) { GimpBlobPoint points[4]; /* Make sure we order points ccw */ if (xp * yq - yq * xp < 0) { xq = -xq; yq = -yq; } .... } PVS-Studio's diagnostic message: V501 There are identical sub-expressions to the left and to the right of the '-' operator: xp * yq - yq * xp gimpink-blob.c 162 The "xp * yq - yq * xp" expression is very strange. The value "xp*yq" is subtracted from itself. Similar checks can be found a bit further in this file. Look for lines 195 and 278. Fragment No. 4 gint64 gimp_g_value_get_memsize (GValue *value) { .... GimpArray *array = g_value_get_boxed (value); if (array) memsize += (sizeof (GimpArray) + array->static_data ? 0 : array->length); ....
  • 3. } PVS-Studio's diagnostic message: V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '+' operator. gimp-utils.c 233 There is a mess in operator precedence. 0 or "array->length" must be added to the size of some object. But the '+' operator's priority is higher than that of '?:'. The expression therefore will execute in the following way: memsize += ((sizeof (GimpArray) + array->static_data) ? 0 : array->length); The programmer seems to have known about it, that's why he used parentheses. But then one of them is in a wrong place. The correct code should look as follows: memsize += sizeof (GimpArray) + (array->static_data ? 0 : array->length); Fragments No. 5, No. 6 #define cmsFLAGS_NOOPTIMIZE 0x0100 #define cmsFLAGS_BLACKPOINTCOMPENSATION 0x2000 static void lcms_layers_transform_rgb (...., gboolean bpc) { .... transform = cmsCreateTransform ( src_profile, lcms_format, dest_profile, lcms_format, intent, cmsFLAGS_NOOPTIMIZE | bpc ? cmsFLAGS_BLACKPOINTCOMPENSATION : 0); .... } PVS-Studio's diagnostic message: V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '|' operator. lcms.c 1016 Depending on the 'bpc' variable, the function should receive either the "cmsFLAGS_BLACKPOINTCOMPENSATION" flag or a combination of flags "cmsFLAGS_BLACKPOINTCOMPENSATION | cmsFLAGS_NOOPTIMIZE".
  • 4. The '|' operator's priority is higher than that of the ternary operator '?:'. As a result, the '?:' operator has the "cmsFLAGS_NOOPTIMIZE | bpc" expression as its condition. And this condition is always true. The function always receives the cmsFLAGS_BLACKPOINTCOMPENSATION flag. The correct code should look like this: transform = cmsCreateTransform ( src_profile, lcms_format, dest_profile, lcms_format, intent, cmsFLAGS_NOOPTIMIZE | (bpc ? cmsFLAGS_BLACKPOINTCOMPENSATION : 0)); The same error can be found in lcms.c 1016. Fragment No. 7 static gint load_resource_lrfx (....) { .... else if (memcmp (effectname, "oglw", 4) == 0) <<<=== .... else if (memcmp (effectname, "iglw", 4) == 0) .... else if (memcmp (effectname, "oglw", 4) == 0) <<<=== .... else if (memcmp (effectname, "bevl", 4) == 0) .... } PVS-Studio's diagnostic message: V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 602, 688. psd-layer-res-load.c 602 Two identical conditions in the if-elseif-elseif-... sequence. Fragment No. 8 void gimp_text_get_transformation (GimpText *text, GimpMatrix3 *matrix)
  • 5. { g_return_if_fail (GIMP_IS_TEXT (text)); g_return_if_fail (matrix != NULL); matrix->coeff[0][0] = text->transformation.coeff[0][0]; matrix->coeff[0][1] = text->transformation.coeff[0][1]; matrix->coeff[0][2] = text->offset_x; matrix->coeff[1][0] = text->transformation.coeff[1][0]; matrix->coeff[1][1] = text->transformation.coeff[1][1]; matrix->coeff[1][2] = text->offset_y; matrix->coeff[2][0] = 0.0; matrix->coeff[2][1] = 0.0; matrix->coeff[2][1] = 1.0; <<<=== } PVS-Studio's diagnostic message: V519 The 'matrix->coeff[2][1]' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 567, 568. gimptext.c 568 The Last Line Effect. In the very end, an incorrect index is used. It should be like this: matrix->coeff[2][0] = 0.0; matrix->coeff[2][1] = 0.0; matrix->coeff[2][2] = 1.0; Fragment No. 9 static void warp_one (....) { .... if (first_time) gimp_pixel_rgn_init (&dest_rgn, new, x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE); else gimp_pixel_rgn_init (&dest_rgn, new, x1, y1, (x2 - x1), (y2 - y1),
  • 6. TRUE, TRUE); .... } PVS-Studio's diagnostic message: V523 The 'then' statement is equivalent to the 'else' statement. warp.c 1366 It is very suspicious that one and the same branch is executed regardless of the condition. Fragments No. 10, No. 11, No. 12 gboolean gimp_wire_read (GIOChannel *channel, guint8 *buf, gsize count, gpointer user_data) { g_return_val_if_fail (count >= 0, FALSE); .... } PVS-Studio's diagnostic message: V547 Expression 'count >= 0' is always true. Unsigned type value is always >= 0. gimpwire.c 99 The "count >= 0" check doesn't make sense as the 'count' variable is unsigned. Perhaps it's not a serious bug but I still should mention it. Similar checks: gimpwire.c 170; gimpcageconfig.c 428. Below we will discuss more interesting issues found through the V547 diagnostic. Fragment No. 13 static GimpPlugInImageType image_types_parse (const gchar *name, const gchar *image_types) { .... while (*image_types && ((*image_types != ' ') || (*image_types != 't') || (*image_types != ','))) {
  • 7. image_types++; } .... } PVS-Studio's diagnostic message: V547 Expression is always true. Probably the '&&' operator should be used here. gimppluginprocedure.c 808 To make it clearer, I've made an artificial example: int A = ...; if ( A != 1 || A != 2 || A != 3) Regardless of the value the A variable takes, the condition is always true. Fragment No. 14 static gunichar basic_inchar(port *pt) { .... gunichar c; .... c = g_utf8_get_char_validated(pt->rep.string.curr, len); if (c >= 0) /* Valid UTF-8 character? */ { len = g_unichar_to_utf8(c, NULL); pt->rep.string.curr += len; return c; } /* Look for next valid UTF-8 character in buffer */ pt->rep.string.curr = g_utf8_find_next_char( pt->rep.string.curr, pt->rep.string.past_the_end); .... } PVS-Studio's diagnostic message: V547 Expression 'c >= 0' is always true. Unsigned type value is always >= 0. scheme.c 1654 All the characters will be treated as correct UTF-8 characters. The 'c' variable is unsigned, so the (c >= 0) condition is always true.
  • 8. Fragment No. 15 #define ABS(a) (((a) < 0) ? -(a) : (a)) static gint32 load_thumbnail (...., gint32 thumb_size, ....) { .... guint32 size; guint32 diff; .... diff = ABS(thumb_size - size); .... } PVS-Studio's diagnostic message: V547 Expression '(thumb_size - size) < 0' is always false. Unsigned type value is never < 0. file-xmc.c 874 The program works differently than the programmer expected. Suppose the 'thumb_size' variable equals 10 and the 'size' variable equals 25. It may seem at first that the expression will evaluate to 15. But actually the result will be 0xFFFFFFF1 (4294967281). The "thumb_size - size" expression is unsigned. As a result, we'll get number 0xFFFFFFF1u. The ABS macro doesn't do anything in this case. Fragment No. 16 static gchar * script_fu_menu_map (const gchar *menu_path) { .... const gchar *suffix = menu_path + strlen (mapping[i].old); if (! *suffix == '/') continue; .... } PVS-Studio's diagnostic message: V562 It's odd to compare 0 or 1 with a value of 47: !* suffix == '/'. script-fu-scripts.c 859
  • 9. Another trouble with operator precedence. First, the "!*suffix" expression is calculated. Its result is either 0 or 1. This number is then compared to the '/' character, which doesn't make any sense at all. The correct code: if (*suffix != '/') Fragment No. 17 static void save_file_chooser_response (GtkFileChooser *chooser, gint response_id, GFigObj *obj) { .... gfig_context->current_obj = obj; gfig_save_callbk (); gfig_context->current_obj = gfig_context->current_obj; .... } PVS-Studio's diagnostic message: V570 The 'gfig_context->current_obj' variable is assigned to itself. gfig-dialog. c 1623 The variable is copied into itself. Fragment No. 18 size g_strlcpy(gchar *dest, const gchar *src, gsize dest_size); GList * gimp_brush_generated_load (....) { .... gchar *string; .... /* the empty string is not an allowed name */ if (strlen (string) < 1) g_strlcpy (string, _("Untitled"), sizeof (string)); .... }
  • 10. PVS-Studio's diagnostic message: V579 The g_strlcpy function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the third argument. gimpbrushgenerated-load.c 119 The "sizeof(string)" operator calculates the pointer size, not the buffer size. Fragment No. 19 static gboolean save_image (....) { .... gint c; .... if (has_alpha && (data[rowoffset + k + 1] < 128)) c |= 0 << (thisbit ++); else .... } PVS-Studio's diagnostic message: V684 A value of the variable 'c' is not modified. Consider inspecting the expression. It is possible that '1' should be present instead of '0'. file-xbm.c 1136 The "c |= 0 << (thisbit ++);" expression doesn't change the 'c' variable. I've noticed that code like that is very likely to be found when the programmer wanted to zero out a certain bit but made a mistake. Then the code should look as follows: c &= ~(1u << (thisbit ++)); Fragment No. 20 gboolean gimp_item_get_popup_size (...., gint *popup_width, gint *popup_height) { .... if (scaling_up) { *popup_width = gimp_item_get_width (item); *popup_width = gimp_item_get_height (item); } .... }
  • 11. PVS-Studio's diagnostic message: V537 Consider reviewing the correctness of 'popup_width' item's usage. gimpitem-preview.c 126 This is a typo or a consequence of the Copy-Paste technique. The correct code: *popup_width = gimp_item_get_width (item); *popup_height = gimp_item_get_height (item); Fragment No. 21 gboolean gimp_draw_tool_on_vectors_curve (...., GimpAnchor **ret_segment_start, GimpAnchor **ret_segment_end, ....) { .... if (ret_segment_start) *ret_segment_start = NULL; if (ret_segment_start) *ret_segment_end = NULL; .... } PVS-Studio's diagnostic message: V581 The conditional expressions of the 'if' operators situated alongside each other are identical. Check lines: 1212, 1213. gimpdrawtool.c 1213 This is a typo or a consequence of the Copy-Paste technique. The correct code: if (ret_segment_start) *ret_segment_start = NULL; if (ret_segment_end) *ret_segment_end = NULL; Fragments No. 22 – No. 40 ObjectList_t* object_list_append_list(ObjectList_t *des, ObjectList_t *src) { GList *p; for (p = src->list; p; p = p->next) object_list_append(des, object_clone((Object_t*) p->data)); object_list_set_changed(des, (src) ? TRUE : FALSE); return des; }
  • 12. PVS-Studio's diagnostic message: V595 The 'src' pointer was utilized before it was verified against nullptr. Check lines: 536, 538. imap_object.c 536 You may conclude from the "(src) ? TRUE : FALSE" condition that the 'src' pointer may be equal to nullptr. However, this pointer is bravely dereferenced in the "p = src->list" expression a bit earlier, which is an error. There are other fragments that triggered the V595 warning as well. They also need checking: • The 'l' pointer. Check lines: 262, 265. gimpimage-item-list.c 262 • The 'quantobj' pointer. Check lines: 965, 971. gimpimage-convert-type.c 965 • The 'slist' pointer. Check lines: 683, 685. gimpfont.c 683 • The 'dock_window->p->context' pointer. Check lines: 487, 504. gimpdockwindow.c 487 • The 'layer_renderer' pointer. Check lines: 1245, 1275. gimplayertreeview.c 1245 • The 'shell->display' pointer. Check lines: 574, 588. gimpdisplayshell-dnd.c 574 • The 'ops' pointer. Check lines: 265, 267. gimpgegltool.c 265 • The 'dialog' pointer. Check lines: 234, 249. file-save-dialog.c 234 • The 'shell' pointer. Check lines: 738, 763. view-actions.c 738 • The 'fname' pointer. Check lines: 1426, 1437. scheme.c 1426 • The 'sgip->table' pointer. Check lines: 148, 161. sgi-lib.c 148 • The 'sgip->length' pointer. Check lines: 154, 167. sgi-lib.c 154 • The 'pixels' pointer. Check lines: 1482, 1508. psd-load.c 1482 • The 'img_a->alpha_names' pointer. Check lines: 1735, 1741. psd-load.c 1735 • The 'brush' pointer. Check lines: 432, 451. brush.c 432 • The 'curve_list->data' pointer. Check lines: 126, 129. curve.c 126 • The 'outline_list->data' pointer. Check lines: 183, 187. pxl-outline.c 183 • The 'id_ptr' pointer. Check lines: 896, 898. sample-colorize.c 896 Conclusion It's not easy to tell how critical the bugs found in this project are. But I will be pleased if some of them will be fixed thanks to this article. Although I told you in the beginning that I don't like GIMP's interface, I'm still thankful to its authors for their project. Quite a number of images for my articles were made in GIMP. Thank you.