##// END OF EJS Templates
mpatch: avoid integer overflow in combine() (SEC)...
Augie Fackler -
r38253:9c5ced52 4.6.1 stable
parent child Browse files
Show More
@@ -1,372 +1,382 b''
1 /*
1 /*
2 mpatch.c - efficient binary patching for Mercurial
2 mpatch.c - efficient binary patching for Mercurial
3
3
4 This implements a patch algorithm that's O(m + nlog n) where m is the
4 This implements a patch algorithm that's O(m + nlog n) where m is the
5 size of the output and n is the number of patches.
5 size of the output and n is the number of patches.
6
6
7 Given a list of binary patches, it unpacks each into a hunk list,
7 Given a list of binary patches, it unpacks each into a hunk list,
8 then combines the hunk lists with a treewise recursion to form a
8 then combines the hunk lists with a treewise recursion to form a
9 single hunk list. This hunk list is then applied to the original
9 single hunk list. This hunk list is then applied to the original
10 text.
10 text.
11
11
12 The text (or binary) fragments are copied directly from their source
12 The text (or binary) fragments are copied directly from their source
13 Python objects into a preallocated output string to avoid the
13 Python objects into a preallocated output string to avoid the
14 allocation of intermediate Python objects. Working memory is about 2x
14 allocation of intermediate Python objects. Working memory is about 2x
15 the total number of hunks.
15 the total number of hunks.
16
16
17 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
17 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
18
18
19 This software may be used and distributed according to the terms
19 This software may be used and distributed according to the terms
20 of the GNU General Public License, incorporated herein by reference.
20 of the GNU General Public License, incorporated herein by reference.
21 */
21 */
22
22
23 #include <limits.h>
23 #include <limits.h>
24 #include <stdlib.h>
24 #include <stdlib.h>
25 #include <string.h>
25 #include <string.h>
26
26
27 #include "bitmanipulation.h"
27 #include "bitmanipulation.h"
28 #include "compat.h"
28 #include "compat.h"
29 #include "mpatch.h"
29 #include "mpatch.h"
30
30
31 /* VC9 doesn't include bool and lacks stdbool.h based on cext/util.h */
31 /* VC9 doesn't include bool and lacks stdbool.h based on cext/util.h */
32 #if defined(_MSC_VER) || __STDC_VERSION__ < 199901L
32 #if defined(_MSC_VER) || __STDC_VERSION__ < 199901L
33 #define true 1
33 #define true 1
34 #define false 0
34 #define false 0
35 typedef unsigned char bool;
35 typedef unsigned char bool;
36 #else
36 #else
37 #include <stdbool.h>
37 #include <stdbool.h>
38 #endif
38 #endif
39
39
40 static struct mpatch_flist *lalloc(ssize_t size)
40 static struct mpatch_flist *lalloc(ssize_t size)
41 {
41 {
42 struct mpatch_flist *a = NULL;
42 struct mpatch_flist *a = NULL;
43
43
44 if (size < 1)
44 if (size < 1)
45 size = 1;
45 size = 1;
46
46
47 a = (struct mpatch_flist *)malloc(sizeof(struct mpatch_flist));
47 a = (struct mpatch_flist *)malloc(sizeof(struct mpatch_flist));
48 if (a) {
48 if (a) {
49 a->base = (struct mpatch_frag *)malloc(
49 a->base = (struct mpatch_frag *)malloc(
50 sizeof(struct mpatch_frag) * size);
50 sizeof(struct mpatch_frag) * size);
51 if (a->base) {
51 if (a->base) {
52 a->head = a->tail = a->base;
52 a->head = a->tail = a->base;
53 return a;
53 return a;
54 }
54 }
55 free(a);
55 free(a);
56 }
56 }
57 return NULL;
57 return NULL;
58 }
58 }
59
59
60 void mpatch_lfree(struct mpatch_flist *a)
60 void mpatch_lfree(struct mpatch_flist *a)
61 {
61 {
62 if (a) {
62 if (a) {
63 free(a->base);
63 free(a->base);
64 free(a);
64 free(a);
65 }
65 }
66 }
66 }
67
67
68 static ssize_t lsize(struct mpatch_flist *a)
68 static ssize_t lsize(struct mpatch_flist *a)
69 {
69 {
70 return a->tail - a->head;
70 return a->tail - a->head;
71 }
71 }
72
72
73 /* add helper to add src and *dest iff it won't overflow */
73 /* add helper to add src and *dest iff it won't overflow */
74 static inline bool safeadd(int src, int *dest)
74 static inline bool safeadd(int src, int *dest)
75 {
75 {
76 if ((src > 0) == (*dest > 0)) {
76 if ((src > 0) == (*dest > 0)) {
77 if (*dest > 0) {
77 if (*dest > 0) {
78 if (src > (INT_MAX - *dest)) {
78 if (src > (INT_MAX - *dest)) {
79 return false;
79 return false;
80 }
80 }
81 } else {
81 } else {
82 if (src < (INT_MIN - *dest)) {
82 if (src < (INT_MIN - *dest)) {
83 return false;
83 return false;
84 }
84 }
85 }
85 }
86 }
86 }
87 *dest += src;
87 *dest += src;
88 return true;
88 return true;
89 }
89 }
90
90
91 /* subtract src from dest and store result in dest */
91 /* subtract src from dest and store result in dest */
92 static inline bool safesub(int src, int *dest)
92 static inline bool safesub(int src, int *dest)
93 {
93 {
94 if (((src > 0) && (*dest < INT_MIN + src)) ||
94 if (((src > 0) && (*dest < INT_MIN + src)) ||
95 ((src < 0) && (*dest > INT_MAX + src))) {
95 ((src < 0) && (*dest > INT_MAX + src))) {
96 return false;
96 return false;
97 }
97 }
98 *dest -= src;
98 *dest -= src;
99 return true;
99 return true;
100 }
100 }
101
101
102 /* move hunks in source that are less cut to dest, compensating
102 /* move hunks in source that are less cut to dest, compensating
103 for changes in offset. the last hunk may be split if necessary.
103 for changes in offset. the last hunk may be split if necessary.
104 */
104 */
105 static int gather(struct mpatch_flist *dest, struct mpatch_flist *src, int cut,
105 static int gather(struct mpatch_flist *dest, struct mpatch_flist *src, int cut,
106 int offset)
106 int offset)
107 {
107 {
108 struct mpatch_frag *d = dest->tail, *s = src->head;
108 struct mpatch_frag *d = dest->tail, *s = src->head;
109 int postend, c, l;
109 int postend, c, l;
110
110
111 while (s != src->tail) {
111 while (s != src->tail) {
112 int soffset = s->start;
112 int soffset = s->start;
113 if (!safeadd(offset, &soffset))
113 if (!safeadd(offset, &soffset))
114 break; /* add would overflow, oh well */
114 break; /* add would overflow, oh well */
115 if (soffset >= cut)
115 if (soffset >= cut)
116 break; /* we've gone far enough */
116 break; /* we've gone far enough */
117
117
118 postend = offset;
118 postend = offset;
119 if (!safeadd(s->start, &postend) ||
119 if (!safeadd(s->start, &postend) ||
120 !safeadd(s->len, &postend)) {
120 !safeadd(s->len, &postend)) {
121 break;
121 break;
122 }
122 }
123 if (postend <= cut) {
123 if (postend <= cut) {
124 /* save this hunk */
124 /* save this hunk */
125 int tmp = s->start;
125 int tmp = s->start;
126 if (!safesub(s->end, &tmp)) {
126 if (!safesub(s->end, &tmp)) {
127 break;
127 break;
128 }
128 }
129 if (!safeadd(s->len, &tmp)) {
129 if (!safeadd(s->len, &tmp)) {
130 break;
130 break;
131 }
131 }
132 if (!safeadd(tmp, &offset)) {
132 if (!safeadd(tmp, &offset)) {
133 break; /* add would overflow, oh well */
133 break; /* add would overflow, oh well */
134 }
134 }
135 *d++ = *s++;
135 *d++ = *s++;
136 } else {
136 } else {
137 /* break up this hunk */
137 /* break up this hunk */
138 c = cut;
138 c = cut;
139 if (!safesub(offset, &c)) {
139 if (!safesub(offset, &c)) {
140 break;
140 break;
141 }
141 }
142 if (s->end < c)
142 if (s->end < c)
143 c = s->end;
143 c = s->end;
144 l = cut - offset - s->start;
144 l = cut - offset - s->start;
145 if (s->len < l)
145 if (s->len < l)
146 l = s->len;
146 l = s->len;
147
147
148 offset += s->start + l - c;
148 offset += s->start + l - c;
149
149
150 d->start = s->start;
150 d->start = s->start;
151 d->end = c;
151 d->end = c;
152 d->len = l;
152 d->len = l;
153 d->data = s->data;
153 d->data = s->data;
154 d++;
154 d++;
155 s->start = c;
155 s->start = c;
156 s->len = s->len - l;
156 s->len = s->len - l;
157 s->data = s->data + l;
157 s->data = s->data + l;
158
158
159 break;
159 break;
160 }
160 }
161 }
161 }
162
162
163 dest->tail = d;
163 dest->tail = d;
164 src->head = s;
164 src->head = s;
165 return offset;
165 return offset;
166 }
166 }
167
167
168 /* like gather, but with no output list */
168 /* like gather, but with no output list */
169 static int discard(struct mpatch_flist *src, int cut, int offset)
169 static int discard(struct mpatch_flist *src, int cut, int offset)
170 {
170 {
171 struct mpatch_frag *s = src->head;
171 struct mpatch_frag *s = src->head;
172 int postend, c, l;
172 int postend, c, l;
173
173
174 while (s != src->tail) {
174 while (s != src->tail) {
175 int cmpcut = s->start;
175 int cmpcut = s->start;
176 if (!safeadd(offset, &cmpcut)) {
176 if (!safeadd(offset, &cmpcut)) {
177 break;
177 break;
178 }
178 }
179 if (cmpcut >= cut)
179 if (cmpcut >= cut)
180 break;
180 break;
181
181
182 postend = offset;
182 postend = offset;
183 if (!safeadd(s->start, &postend)) {
183 if (!safeadd(s->start, &postend)) {
184 break;
184 break;
185 }
185 }
186 if (!safeadd(s->len, &postend)) {
186 if (!safeadd(s->len, &postend)) {
187 break;
187 break;
188 }
188 }
189 if (postend <= cut) {
189 if (postend <= cut) {
190 /* do the subtraction first to avoid UB integer overflow
190 /* do the subtraction first to avoid UB integer overflow
191 */
191 */
192 int tmp = s->start;
192 int tmp = s->start;
193 if (!safesub(s->end, &tmp)) {
193 if (!safesub(s->end, &tmp)) {
194 break;
194 break;
195 }
195 }
196 if (!safeadd(s->len, &tmp)) {
196 if (!safeadd(s->len, &tmp)) {
197 break;
197 break;
198 }
198 }
199 if (!safeadd(tmp, &offset)) {
199 if (!safeadd(tmp, &offset)) {
200 break;
200 break;
201 }
201 }
202 s++;
202 s++;
203 } else {
203 } else {
204 c = cut;
204 c = cut;
205 if (!safesub(offset, &c)) {
205 if (!safesub(offset, &c)) {
206 break;
206 break;
207 }
207 }
208 if (s->end < c)
208 if (s->end < c)
209 c = s->end;
209 c = s->end;
210 l = cut - offset - s->start;
210 l = cut - offset - s->start;
211 if (s->len < l)
211 if (s->len < l)
212 l = s->len;
212 l = s->len;
213
213
214 offset += s->start + l - c;
214 offset += s->start + l - c;
215 s->start = c;
215 s->start = c;
216 s->len = s->len - l;
216 s->len = s->len - l;
217 s->data = s->data + l;
217 s->data = s->data + l;
218
218
219 break;
219 break;
220 }
220 }
221 }
221 }
222
222
223 src->head = s;
223 src->head = s;
224 return offset;
224 return offset;
225 }
225 }
226
226
227 /* combine hunk lists a and b, while adjusting b for offset changes in a/
227 /* combine hunk lists a and b, while adjusting b for offset changes in a/
228 this deletes a and b and returns the resultant list. */
228 this deletes a and b and returns the resultant list. */
229 static struct mpatch_flist *combine(struct mpatch_flist *a,
229 static struct mpatch_flist *combine(struct mpatch_flist *a,
230 struct mpatch_flist *b)
230 struct mpatch_flist *b)
231 {
231 {
232 struct mpatch_flist *c = NULL;
232 struct mpatch_flist *c = NULL;
233 struct mpatch_frag *bh, *ct;
233 struct mpatch_frag *bh, *ct;
234 int offset = 0, post;
234 int offset = 0, post;
235
235
236 if (a && b)
236 if (a && b)
237 c = lalloc((lsize(a) + lsize(b)) * 2);
237 c = lalloc((lsize(a) + lsize(b)) * 2);
238
238
239 if (c) {
239 if (c) {
240
240
241 for (bh = b->head; bh != b->tail; bh++) {
241 for (bh = b->head; bh != b->tail; bh++) {
242 /* save old hunks */
242 /* save old hunks */
243 offset = gather(c, a, bh->start, offset);
243 offset = gather(c, a, bh->start, offset);
244
244
245 /* discard replaced hunks */
245 /* discard replaced hunks */
246 post = discard(a, bh->end, offset);
246 post = discard(a, bh->end, offset);
247
247
248 /* insert new hunk */
248 /* insert new hunk */
249 ct = c->tail;
249 ct = c->tail;
250 ct->start = bh->start - offset;
250 ct->start = bh->start;
251 ct->end = bh->end - post;
251 ct->end = bh->end;
252 if (!safesub(offset, &(ct->start)) ||
253 !safesub(post, &(ct->end))) {
254 /* It was already possible to exit
255 * this function with a return value
256 * of NULL before the safesub()s were
257 * added, so this should be fine. */
258 mpatch_lfree(c);
259 c = NULL;
260 goto done;
261 }
252 ct->len = bh->len;
262 ct->len = bh->len;
253 ct->data = bh->data;
263 ct->data = bh->data;
254 c->tail++;
264 c->tail++;
255 offset = post;
265 offset = post;
256 }
266 }
257
267
258 /* hold on to tail from a */
268 /* hold on to tail from a */
259 memcpy(c->tail, a->head, sizeof(struct mpatch_frag) * lsize(a));
269 memcpy(c->tail, a->head, sizeof(struct mpatch_frag) * lsize(a));
260 c->tail += lsize(a);
270 c->tail += lsize(a);
261 }
271 }
262
272 done:
263 mpatch_lfree(a);
273 mpatch_lfree(a);
264 mpatch_lfree(b);
274 mpatch_lfree(b);
265 return c;
275 return c;
266 }
276 }
267
277
268 /* decode a binary patch into a hunk list */
278 /* decode a binary patch into a hunk list */
269 int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist **res)
279 int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist **res)
270 {
280 {
271 struct mpatch_flist *l;
281 struct mpatch_flist *l;
272 struct mpatch_frag *lt;
282 struct mpatch_frag *lt;
273 int pos = 0;
283 int pos = 0;
274
284
275 /* assume worst case size, we won't have many of these lists */
285 /* assume worst case size, we won't have many of these lists */
276 l = lalloc(len / 12 + 1);
286 l = lalloc(len / 12 + 1);
277 if (!l)
287 if (!l)
278 return MPATCH_ERR_NO_MEM;
288 return MPATCH_ERR_NO_MEM;
279
289
280 lt = l->tail;
290 lt = l->tail;
281
291
282 /* We check against len-11 to ensure we have at least 12 bytes
292 /* We check against len-11 to ensure we have at least 12 bytes
283 left in the patch so we can read our three be32s out of it. */
293 left in the patch so we can read our three be32s out of it. */
284 while (pos >= 0 && pos < (len - 11)) {
294 while (pos >= 0 && pos < (len - 11)) {
285 lt->start = getbe32(bin + pos);
295 lt->start = getbe32(bin + pos);
286 lt->end = getbe32(bin + pos + 4);
296 lt->end = getbe32(bin + pos + 4);
287 lt->len = getbe32(bin + pos + 8);
297 lt->len = getbe32(bin + pos + 8);
288 if (lt->start < 0 || lt->start > lt->end || lt->len < 0)
298 if (lt->start < 0 || lt->start > lt->end || lt->len < 0)
289 break; /* sanity check */
299 break; /* sanity check */
290 if (!safeadd(12, &pos)) {
300 if (!safeadd(12, &pos)) {
291 break;
301 break;
292 }
302 }
293 lt->data = bin + pos;
303 lt->data = bin + pos;
294 if (!safeadd(lt->len, &pos)) {
304 if (!safeadd(lt->len, &pos)) {
295 break;
305 break;
296 }
306 }
297 lt++;
307 lt++;
298 }
308 }
299
309
300 if (pos != len) {
310 if (pos != len) {
301 mpatch_lfree(l);
311 mpatch_lfree(l);
302 return MPATCH_ERR_CANNOT_BE_DECODED;
312 return MPATCH_ERR_CANNOT_BE_DECODED;
303 }
313 }
304
314
305 l->tail = lt;
315 l->tail = lt;
306 *res = l;
316 *res = l;
307 return 0;
317 return 0;
308 }
318 }
309
319
310 /* calculate the size of resultant text */
320 /* calculate the size of resultant text */
311 ssize_t mpatch_calcsize(ssize_t len, struct mpatch_flist *l)
321 ssize_t mpatch_calcsize(ssize_t len, struct mpatch_flist *l)
312 {
322 {
313 ssize_t outlen = 0, last = 0;
323 ssize_t outlen = 0, last = 0;
314 struct mpatch_frag *f = l->head;
324 struct mpatch_frag *f = l->head;
315
325
316 while (f != l->tail) {
326 while (f != l->tail) {
317 if (f->start < last || f->end > len) {
327 if (f->start < last || f->end > len) {
318 return MPATCH_ERR_INVALID_PATCH;
328 return MPATCH_ERR_INVALID_PATCH;
319 }
329 }
320 outlen += f->start - last;
330 outlen += f->start - last;
321 last = f->end;
331 last = f->end;
322 outlen += f->len;
332 outlen += f->len;
323 f++;
333 f++;
324 }
334 }
325
335
326 outlen += len - last;
336 outlen += len - last;
327 return outlen;
337 return outlen;
328 }
338 }
329
339
330 int mpatch_apply(char *buf, const char *orig, ssize_t len,
340 int mpatch_apply(char *buf, const char *orig, ssize_t len,
331 struct mpatch_flist *l)
341 struct mpatch_flist *l)
332 {
342 {
333 struct mpatch_frag *f = l->head;
343 struct mpatch_frag *f = l->head;
334 int last = 0;
344 int last = 0;
335 char *p = buf;
345 char *p = buf;
336
346
337 while (f != l->tail) {
347 while (f != l->tail) {
338 if (f->start < last || f->start > len || f->end > len ||
348 if (f->start < last || f->start > len || f->end > len ||
339 last < 0) {
349 last < 0) {
340 return MPATCH_ERR_INVALID_PATCH;
350 return MPATCH_ERR_INVALID_PATCH;
341 }
351 }
342 memcpy(p, orig + last, f->start - last);
352 memcpy(p, orig + last, f->start - last);
343 p += f->start - last;
353 p += f->start - last;
344 memcpy(p, f->data, f->len);
354 memcpy(p, f->data, f->len);
345 last = f->end;
355 last = f->end;
346 p += f->len;
356 p += f->len;
347 f++;
357 f++;
348 }
358 }
349 if (last < 0) {
359 if (last < 0) {
350 return MPATCH_ERR_INVALID_PATCH;
360 return MPATCH_ERR_INVALID_PATCH;
351 }
361 }
352 memcpy(p, orig + last, len - last);
362 memcpy(p, orig + last, len - last);
353 return 0;
363 return 0;
354 }
364 }
355
365
356 /* recursively generate a patch of all bins between start and end */
366 /* recursively generate a patch of all bins between start and end */
357 struct mpatch_flist *
367 struct mpatch_flist *
358 mpatch_fold(void *bins, struct mpatch_flist *(*get_next_item)(void *, ssize_t),
368 mpatch_fold(void *bins, struct mpatch_flist *(*get_next_item)(void *, ssize_t),
359 ssize_t start, ssize_t end)
369 ssize_t start, ssize_t end)
360 {
370 {
361 ssize_t len;
371 ssize_t len;
362
372
363 if (start + 1 == end) {
373 if (start + 1 == end) {
364 /* trivial case, output a decoded list */
374 /* trivial case, output a decoded list */
365 return get_next_item(bins, start);
375 return get_next_item(bins, start);
366 }
376 }
367
377
368 /* divide and conquer, memory management is elsewhere */
378 /* divide and conquer, memory management is elsewhere */
369 len = (end - start) / 2;
379 len = (end - start) / 2;
370 return combine(mpatch_fold(bins, get_next_item, start, start + len),
380 return combine(mpatch_fold(bins, get_next_item, start, start + len),
371 mpatch_fold(bins, get_next_item, start + len, end));
381 mpatch_fold(bins, get_next_item, start + len, end));
372 }
382 }
General Comments 0
You need to be logged in to leave comments. Login now