Opened 7 years ago
Closed 6 years ago
#3564 closed defect (fixed)
Inconsistent results from qsort callback in g.mkfontcap
Reported by: | yugr | Owned by: | |
---|---|---|---|
Priority: | normal | Milestone: | 7.4.2 |
Component: | Default | Version: | 7.4.0 |
Keywords: | qsort callback | Cc: | |
CPU: | All | Platform: | All |
Description
Hi,
qsort callback compare_fonts in g.mkfontcap may return invalid result when arguments are swapped. Such bugs may causes inconsistent order or even crashes in some qsort implementations (https://bugzilla.samba.org/show_bug.cgi?id=3959).
The issue has been detected when running standard testsuite under SortChecker? (https://github.com/yugr/sortcheck):
g.mkfontcap[15109]: qsort: comparison function is not symmetric (comparison function 0x4023c0 (/build/grass-7.0.3/dist.x86_64-pc-linux-gnu/bin/g.mkfontcap+0x4023c0), called from 0x4017a8 (/build/grass-7.0.3/dist.x86_64-pc-linux-gnu/bin/g.mkfontcap+0x4017a8), cmdline is "/build/grass-7.0.3/dist.x86_64-pc-linux-gnu/bin/g.mkfontcap -s")
Problem is in lines
if (aa->type != bb->type)
return (aa->type > bb->type);
which should be replaced with something like
if (aa->type != bb->type)
return (aa->type > bb->type) ? 1 : -1;
As a side note, many qsort callbacks in Grass are vulnerable to integer overflows e.g. cmp_edge in ./lib/vector/neta/spanningtree.c:
return ((edge_cost_pair *) pa)->cost - ((edge_cost_pair *) pb)->cost;
or longcmp in ./raster/r.kappa/prt_mat.c:
return (*a - *b);
and many many others.
Change History (13)
comment:1 by , 7 years ago
Keywords: | qsort callback added |
---|---|
Milestone: | → 7.6.0 |
follow-up: 3 comment:2 by , 7 years ago
Hi Markus,
If you already have a list of these many other problems, can you provide such a list? That would be very helpful.
I wasn't sure you'd be interested in these potential overflows (e.g. some may be highly unlikely due to small subtracted values) so didn't report them in first comment.
There you go (found by manual analysis of source code):
./lib/vector/neta/spanningtree.c static int cmp_edge(const void *pa, const void *pb) { return ((edge_cost_pair *) pa)->cost - ((edge_cost_pair *) pb)->cost; } ./lib/vector/Vlib/break_lines.c static int cmp(const void *a, const void *b) { int ai = *(int *)a; int bi = *(int *)b; return (ai - bi); } ./raster/r.distance/edges.c static int cmp(const void *aa, const void *bb) { const struct CatEdgeList *a = aa, *b = bb; return (int)(a->cat - b->cat); } ./raster/r.kappa/prt_mat.c static int longcomp(const void *aa, const void *bb) { const long *a = aa; const long *b = bb; return (*a - *b); } ./raster/r.stats/stats.c static int node_compare(const void *pp, const void *qq) { struct Node *const *p = pp, *const *q = qq; register int i, x; register const CELL *a, *b; a = (*p)->values; b = (*q)->values; for (i = nfiles; --i >= 0;) if (x = (*a++ - *b++), x) return x; return 0; } ./raster/r.what/main.c static int by_row(const void *ii, const void *jj) { const struct order *i = ii, *j = jj; return i->row - j->row; } ./vector/v.generalize/misc.c static int cmp(const void *a, const void *b) { int ai = *(int *)a; int bi = *(int *)b; return (ai - bi); } ./vector/v.overlay/area_area.c static int cmp_int(const void *a, const void *b) { return (*(int *)a - *(int *)b); } ./vector/v.to.rast/support.c static int cmp_labels_i(const void *a, const void *b) { struct My_labels_rule *al = (struct My_labels_rule *) a; struct My_labels_rule *bl = (struct My_labels_rule *) b; return (al->i - bl->i); } ./vector/v.vect.stats/main.c static int cmp_area(const void *pa, const void *pb) { AREA_CAT *p1 = (AREA_CAT *) pa; AREA_CAT *p2 = (AREA_CAT *) pb; return (p1->area_cat - p2->area_cat); } ./vector/v.what.rast/search.c ./vector/v.what.rast3/search.c /* for qsort, order list by row */ int by_row(const void *ii, const void *jj) { const struct order *i = ii, *j = jj; return i->row - j->row; } /* for qsort, order list by cat */ int by_cat(const void *ii, const void *jj) { const struct order *i = ii, *j = jj; return i->cat - j->cat; }
comment:3 by , 7 years ago
Replying to yugr:
Hi Markus,
If you already have a list of these many other problems, can you provide such a list? That would be very helpful.
I wasn't sure you'd be interested in these potential overflows (e.g. some may be highly unlikely due to small subtracted values) so didn't report them in first comment.
I am interested. The comparison in g.mkfontcap was wrong, it returned 0 when it should return -1. The comparison in r.kappa was clearly dangerous because it converted long to int. The remaining cases need to be checked individually: if the integers to be subtracted are always positive, integer overflow can not happen.
Thanks for the list!
comment:4 by , 7 years ago
One more symmetry issue in ./vector/v.profile/main.c:
static int compdist(const void *d1, const void *d2) { ... if (r1->distance > r2->distance) return 1; else return -1; }
comment:11 by , 6 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
All instances of possible inconsistent results or potential buffer overflow have been fixed.
follow-up: 13 comment:12 by , 6 years ago
Resolution: | fixed |
---|---|
Status: | closed → reopened |
Do(n't) we want a bulk backport? If yes, I can do that
comment:13 by , 6 years ago
Milestone: | 7.6.0 → 7.4.2 |
---|---|
Resolution: | → fixed |
Status: | reopened → closed |
Replying to yugr:
Fixed in trunk r72727.
Fixed in trunk r72728.
Fixed in trunk r72729.
If you already have a list of these many other problems, can you provide such a list? That would be very helpful.