summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-04-02 20:24:09 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-04-02 20:24:09 +0000
commita7f4e4bbd8d7a47a49404d28bc07ab3b5dc1c19b (patch)
tree3ec79d4f425e7a5977cb54d0bf83e3eb615fa786
parent2e4c3c4cc3c2f6bdd3bfbafe9980f46b24971009 (diff)
downloadbusybox-w32-a7f4e4bbd8d7a47a49404d28bc07ab3b5dc1c19b.tar.gz
busybox-w32-a7f4e4bbd8d7a47a49404d28bc07ab3b5dc1c19b.tar.bz2
busybox-w32-a7f4e4bbd8d7a47a49404d28bc07ab3b5dc1c19b.zip
expr: fix comparisons 'a < b' where we were overflowing a-b
(not to mention that we used int, not arith_t). closes bug 2744. Also, shrink a bit and add testsuite entry function old new delta nextarg 75 84 +9 tostring 38 35 -3 toarith 89 86 -3 str_value 35 32 -3 eval6 555 552 -3 int_value 29 23 -6 eval4 128 120 -8 eval3 112 104 -8 eval2 512 417 -95 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/8 up/down: 9/-129) Total: -120 bytes
-rw-r--r--coreutils/expr.c171
-rw-r--r--testsuite/expr/expr-big16
2 files changed, 100 insertions, 87 deletions
diff --git a/coreutils/expr.c b/coreutils/expr.c
index 959f5200c..2f9c5c1e3 100644
--- a/coreutils/expr.c
+++ b/coreutils/expr.c
@@ -28,13 +28,6 @@
28#include "libbb.h" 28#include "libbb.h"
29#include "xregex.h" 29#include "xregex.h"
30 30
31/* The kinds of value we can have. */
32enum valtype {
33 integer,
34 string
35};
36typedef enum valtype TYPE;
37
38#if ENABLE_EXPR_MATH_SUPPORT_64 31#if ENABLE_EXPR_MATH_SUPPORT_64
39typedef int64_t arith_t; 32typedef int64_t arith_t;
40 33
@@ -51,10 +44,16 @@ typedef long arith_t;
51 44
52/* TODO: use bb_strtol[l]? It's easier to check for errors... */ 45/* TODO: use bb_strtol[l]? It's easier to check for errors... */
53 46
47/* The kinds of value we can have. */
48enum {
49 INTEGER,
50 STRING
51};
52
54/* A value is.... */ 53/* A value is.... */
55struct valinfo { 54struct valinfo {
56 TYPE type; /* Which kind. */ 55 smallint type; /* Which kind. */
57 union { /* The value itself. */ 56 union { /* The value itself. */
58 arith_t i; 57 arith_t i;
59 char *s; 58 char *s;
60 } u; 59 } u;
@@ -77,8 +76,9 @@ static VALUE *int_value(arith_t i)
77{ 76{
78 VALUE *v; 77 VALUE *v;
79 78
80 v = xmalloc(sizeof(VALUE)); 79 v = xzalloc(sizeof(VALUE));
81 v->type = integer; 80 if (INTEGER) /* otherwise xzaaloc did it already */
81 v->type = INTEGER;
82 v->u.i = i; 82 v->u.i = i;
83 return v; 83 return v;
84} 84}
@@ -89,46 +89,47 @@ static VALUE *str_value(const char *s)
89{ 89{
90 VALUE *v; 90 VALUE *v;
91 91
92 v = xmalloc(sizeof(VALUE)); 92 v = xzalloc(sizeof(VALUE));
93 v->type = string; 93 if (STRING) /* otherwise xzaaloc did it already */
94 v->type = STRING;
94 v->u.s = xstrdup(s); 95 v->u.s = xstrdup(s);
95 return v; 96 return v;
96} 97}
97 98
98/* Free VALUE V, including structure components. */ 99/* Free VALUE V, including structure components. */
99 100
100static void freev(VALUE * v) 101static void freev(VALUE *v)
101{ 102{
102 if (v->type == string) 103 if (v->type == STRING)
103 free(v->u.s); 104 free(v->u.s);
104 free(v); 105 free(v);
105} 106}
106 107
107/* Return nonzero if V is a null-string or zero-number. */ 108/* Return nonzero if V is a null-string or zero-number. */
108 109
109static int null(VALUE * v) 110static int null(VALUE *v)
110{ 111{
111 if (v->type == integer) 112 if (v->type == INTEGER)
112 return v->u.i == 0; 113 return v->u.i == 0;
113 /* string: */ 114 /* STRING: */
114 return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0'); 115 return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0');
115} 116}
116 117
117/* Coerce V to a string value (can't fail). */ 118/* Coerce V to a STRING value (can't fail). */
118 119
119static void tostring(VALUE * v) 120static void tostring(VALUE *v)
120{ 121{
121 if (v->type == integer) { 122 if (v->type == INTEGER) {
122 v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i); 123 v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i);
123 v->type = string; 124 v->type = STRING;
124 } 125 }
125} 126}
126 127
127/* Coerce V to an integer value. Return 1 on success, 0 on failure. */ 128/* Coerce V to an INTEGER value. Return 1 on success, 0 on failure. */
128 129
129static bool toarith(VALUE * v) 130static bool toarith(VALUE *v)
130{ 131{
131 if (v->type == string) { 132 if (v->type == STRING) {
132 arith_t i; 133 arith_t i;
133 char *e; 134 char *e;
134 135
@@ -139,50 +140,54 @@ static bool toarith(VALUE * v)
139 return 0; 140 return 0;
140 free(v->u.s); 141 free(v->u.s);
141 v->u.i = i; 142 v->u.i = i;
142 v->type = integer; 143 v->type = INTEGER;
143 } 144 }
144 return 1; 145 return 1;
145} 146}
146 147
147/* Return nonzero if the next token matches STR exactly. 148/* Return str[0]+str[1] if the next token matches STR exactly.
148 STR must not be NULL. */ 149 STR must not be NULL. */
149 150
150static bool nextarg(const char *str) 151static int nextarg(const char *str)
151{ 152{
152 if (*G.args == NULL) 153 if (*G.args == NULL || strcmp(*G.args, str) != 0)
153 return 0; 154 return 0;
154 return strcmp(*G.args, str) == 0; 155 return (unsigned char)str[0] + (unsigned char)str[1];
155} 156}
156 157
157/* The comparison operator handling functions. */ 158/* The comparison operator handling functions. */
158 159
159static int cmp_common(VALUE * l, VALUE * r, int op) 160static int cmp_common(VALUE *l, VALUE *r, int op)
160{ 161{
161 int cmpval; 162 arith_t ll, rr;
162 163
163 if (l->type == string || r->type == string) { 164 ll = l->u.i;
165 rr = r->u.i;
166 if (l->type == STRING || r->type == STRING) {
164 tostring(l); 167 tostring(l);
165 tostring(r); 168 tostring(r);
166 cmpval = strcmp(l->u.s, r->u.s); 169 ll = strcmp(l->u.s, r->u.s);
167 } else 170 rr = 0;
168 cmpval = l->u.i - r->u.i; 171 }
172 /* calculating ll - rr and checking the result is prone to overflows.
173 * We'll do it differently: */
169 if (op == '<') 174 if (op == '<')
170 return cmpval < 0; 175 return ll < rr;
171 if (op == ('L' + 'E')) 176 if (op == ('<' + '='))
172 return cmpval <= 0; 177 return ll <= rr;
173 if (op == '=') 178 if (op == '=' || (op == '=' + '='))
174 return cmpval == 0; 179 return ll == rr;
175 if (op == '!') 180 if (op == '!' + '=')
176 return cmpval != 0; 181 return ll != rr;
177 if (op == '>') 182 if (op == '>')
178 return cmpval > 0; 183 return ll > rr;
179 /* >= */ 184 /* >= */
180 return cmpval >= 0; 185 return ll >= rr;
181} 186}
182 187
183/* The arithmetic operator handling functions. */ 188/* The arithmetic operator handling functions. */
184 189
185static arith_t arithmetic_common(VALUE * l, VALUE * r, int op) 190static arith_t arithmetic_common(VALUE *l, VALUE *r, int op)
186{ 191{
187 arith_t li, ri; 192 arith_t li, ri;
188 193
@@ -190,25 +195,24 @@ static arith_t arithmetic_common(VALUE * l, VALUE * r, int op)
190 bb_error_msg_and_die("non-numeric argument"); 195 bb_error_msg_and_die("non-numeric argument");
191 li = l->u.i; 196 li = l->u.i;
192 ri = r->u.i; 197 ri = r->u.i;
193 if ((op == '/' || op == '%') && ri == 0)
194 bb_error_msg_and_die("division by zero");
195 if (op == '+') 198 if (op == '+')
196 return li + ri; 199 return li + ri;
197 else if (op == '-') 200 if (op == '-')
198 return li - ri; 201 return li - ri;
199 else if (op == '*') 202 if (op == '*')
200 return li * ri; 203 return li * ri;
201 else if (op == '/') 204 if (ri == 0)
205 bb_error_msg_and_die("division by zero");
206 if (op == '/')
202 return li / ri; 207 return li / ri;
203 else 208 return li % ri;
204 return li % ri;
205} 209}
206 210
207/* Do the : operator. 211/* Do the : operator.
208 SV is the VALUE for the lhs (the string), 212 SV is the VALUE for the lhs (the string),
209 PV is the VALUE for the rhs (the pattern). */ 213 PV is the VALUE for the rhs (the pattern). */
210 214
211static VALUE *docolon(VALUE * sv, VALUE * pv) 215static VALUE *docolon(VALUE *sv, VALUE *pv)
212{ 216{
213 VALUE *v; 217 VALUE *v;
214 regex_t re_buffer; 218 regex_t re_buffer;
@@ -230,14 +234,16 @@ of a basic regular expression is not portable; it is being ignored", pv->u.s);
230 234
231 /* expr uses an anchored pattern match, so check that there was a 235 /* expr uses an anchored pattern match, so check that there was a
232 * match and that the match starts at offset 0. */ 236 * match and that the match starts at offset 0. */
233 if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH && 237 if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH
234 re_regs[0].rm_so == 0) { 238 && re_regs[0].rm_so == 0
239 ) {
235 /* Were \(...\) used? */ 240 /* Were \(...\) used? */
236 if (re_buffer.re_nsub > 0) { 241 if (re_buffer.re_nsub > 0) {
237 sv->u.s[re_regs[1].rm_eo] = '\0'; 242 sv->u.s[re_regs[1].rm_eo] = '\0';
238 v = str_value(sv->u.s + re_regs[1].rm_so); 243 v = str_value(sv->u.s + re_regs[1].rm_so);
239 } else 244 } else {
240 v = int_value(re_regs[0].rm_eo); 245 v = int_value(re_regs[0].rm_eo);
246 }
241 } else { 247 } else {
242 /* Match failed -- return the right kind of null. */ 248 /* Match failed -- return the right kind of null. */
243 if (re_buffer.re_nsub > 0) 249 if (re_buffer.re_nsub > 0)
@@ -327,7 +333,7 @@ static VALUE *eval6(void)
327 v = str_value(""); 333 v = str_value("");
328 else { 334 else {
329 v = xmalloc(sizeof(VALUE)); 335 v = xmalloc(sizeof(VALUE));
330 v->type = string; 336 v->type = STRING;
331 v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i); 337 v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
332 } 338 }
333 freev(l); 339 freev(l);
@@ -367,14 +373,11 @@ static VALUE *eval4(void)
367 373
368 l = eval5(); 374 l = eval5();
369 while (1) { 375 while (1) {
370 if (nextarg("*")) 376 op = nextarg("*");
371 op = '*'; 377 if (!op) { op = nextarg("/");
372 else if (nextarg("/")) 378 if (!op) { op = nextarg("%");
373 op = '/'; 379 if (!op) return l;
374 else if (nextarg("%")) 380 }}
375 op = '%';
376 else
377 return l;
378 G.args++; 381 G.args++;
379 r = eval5(); 382 r = eval5();
380 val = arithmetic_common(l, r, op); 383 val = arithmetic_common(l, r, op);
@@ -394,12 +397,11 @@ static VALUE *eval3(void)
394 397
395 l = eval4(); 398 l = eval4();
396 while (1) { 399 while (1) {
397 if (nextarg("+")) 400 op = nextarg("+");
398 op = '+'; 401 if (!op) {
399 else if (nextarg("-")) 402 op = nextarg("-");
400 op = '-'; 403 if (!op) return l;
401 else 404 }
402 return l;
403 G.args++; 405 G.args++;
404 r = eval4(); 406 r = eval4();
405 val = arithmetic_common(l, r, op); 407 val = arithmetic_common(l, r, op);
@@ -419,20 +421,15 @@ static VALUE *eval2(void)
419 421
420 l = eval3(); 422 l = eval3();
421 while (1) { 423 while (1) {
422 if (nextarg("<")) 424 op = nextarg("<");
423 op = '<'; 425 if (!op) { op = nextarg("<=");
424 else if (nextarg("<=")) 426 if (!op) { op = nextarg("=");
425 op = 'L' + 'E'; 427 if (!op) { op = nextarg("==");
426 else if (nextarg("=") || nextarg("==")) 428 if (!op) { op = nextarg("!=");
427 op = '='; 429 if (!op) { op = nextarg(">=");
428 else if (nextarg("!=")) 430 if (!op) { op = nextarg(">");
429 op = '!'; 431 if (!op) return l;
430 else if (nextarg(">=")) 432 }}}}}}
431 op = 'G' + 'E';
432 else if (nextarg(">"))
433 op = '>';
434 else
435 return l;
436 G.args++; 433 G.args++;
437 r = eval3(); 434 r = eval3();
438 toarith(l); 435 toarith(l);
@@ -498,7 +495,7 @@ int expr_main(int argc, char **argv)
498 if (*G.args) 495 if (*G.args)
499 bb_error_msg_and_die("syntax error"); 496 bb_error_msg_and_die("syntax error");
500 497
501 if (v->type == integer) 498 if (v->type == INTEGER)
502 printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i); 499 printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i);
503 else 500 else
504 puts(v->u.s); 501 puts(v->u.s);
diff --git a/testsuite/expr/expr-big b/testsuite/expr/expr-big
new file mode 100644
index 000000000..23dbbb3c8
--- /dev/null
+++ b/testsuite/expr/expr-big
@@ -0,0 +1,16 @@
1# busybox expr
2
3# 3*1000*1000*1000 overflows 32-bit signed int
4res=`busybox expr 0 '<' 3000000000`
5[ x"$res" = x1 ] || exit 1
6
7# 9223372036854775807 = 2^31-1
8res=`busybox expr 0 '<' 9223372036854775807`
9[ x"$res" = x1 ] || exit 1
10# coreutils fails this one!
11res=`busybox expr -9223372036854775800 '<' 9223372036854775807`
12[ x"$res" = x1 ] || exit 1
13
14# This one works only by chance
15# res=`busybox expr 0 '<' 9223372036854775808`
16# [ x"$res" = x1 ] || exit 1