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