##// END OF EJS Templates
mpatch: avoid integer overflow in mpatch_decode (SEC)
Augie Fackler -
r38252:59837a16 stable
parent child Browse files
Show More
@@ -1,367 +1,372 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 250 ct->start = bh->start - offset;
251 251 ct->end = bh->end - post;
252 252 ct->len = bh->len;
253 253 ct->data = bh->data;
254 254 c->tail++;
255 255 offset = post;
256 256 }
257 257
258 258 /* hold on to tail from a */
259 259 memcpy(c->tail, a->head, sizeof(struct mpatch_frag) * lsize(a));
260 260 c->tail += lsize(a);
261 261 }
262 262
263 263 mpatch_lfree(a);
264 264 mpatch_lfree(b);
265 265 return c;
266 266 }
267 267
268 268 /* decode a binary patch into a hunk list */
269 269 int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist **res)
270 270 {
271 271 struct mpatch_flist *l;
272 272 struct mpatch_frag *lt;
273 273 int pos = 0;
274 274
275 275 /* assume worst case size, we won't have many of these lists */
276 276 l = lalloc(len / 12 + 1);
277 277 if (!l)
278 278 return MPATCH_ERR_NO_MEM;
279 279
280 280 lt = l->tail;
281 281
282 282 /* We check against len-11 to ensure we have at least 12 bytes
283 283 left in the patch so we can read our three be32s out of it. */
284 284 while (pos >= 0 && pos < (len - 11)) {
285 285 lt->start = getbe32(bin + pos);
286 286 lt->end = getbe32(bin + pos + 4);
287 287 lt->len = getbe32(bin + pos + 8);
288 lt->data = bin + pos + 12;
289 pos += 12 + lt->len;
290 if (lt->start > lt->end || lt->len < 0)
288 if (lt->start < 0 || lt->start > lt->end || lt->len < 0)
291 289 break; /* sanity check */
290 if (!safeadd(12, &pos)) {
291 break;
292 }
293 lt->data = bin + pos;
294 if (!safeadd(lt->len, &pos)) {
295 break;
296 }
292 297 lt++;
293 298 }
294 299
295 300 if (pos != len) {
296 301 mpatch_lfree(l);
297 302 return MPATCH_ERR_CANNOT_BE_DECODED;
298 303 }
299 304
300 305 l->tail = lt;
301 306 *res = l;
302 307 return 0;
303 308 }
304 309
305 310 /* calculate the size of resultant text */
306 311 ssize_t mpatch_calcsize(ssize_t len, struct mpatch_flist *l)
307 312 {
308 313 ssize_t outlen = 0, last = 0;
309 314 struct mpatch_frag *f = l->head;
310 315
311 316 while (f != l->tail) {
312 317 if (f->start < last || f->end > len) {
313 318 return MPATCH_ERR_INVALID_PATCH;
314 319 }
315 320 outlen += f->start - last;
316 321 last = f->end;
317 322 outlen += f->len;
318 323 f++;
319 324 }
320 325
321 326 outlen += len - last;
322 327 return outlen;
323 328 }
324 329
325 330 int mpatch_apply(char *buf, const char *orig, ssize_t len,
326 331 struct mpatch_flist *l)
327 332 {
328 333 struct mpatch_frag *f = l->head;
329 334 int last = 0;
330 335 char *p = buf;
331 336
332 337 while (f != l->tail) {
333 338 if (f->start < last || f->start > len || f->end > len ||
334 339 last < 0) {
335 340 return MPATCH_ERR_INVALID_PATCH;
336 341 }
337 342 memcpy(p, orig + last, f->start - last);
338 343 p += f->start - last;
339 344 memcpy(p, f->data, f->len);
340 345 last = f->end;
341 346 p += f->len;
342 347 f++;
343 348 }
344 349 if (last < 0) {
345 350 return MPATCH_ERR_INVALID_PATCH;
346 351 }
347 352 memcpy(p, orig + last, len - last);
348 353 return 0;
349 354 }
350 355
351 356 /* recursively generate a patch of all bins between start and end */
352 357 struct mpatch_flist *
353 358 mpatch_fold(void *bins, struct mpatch_flist *(*get_next_item)(void *, ssize_t),
354 359 ssize_t start, ssize_t end)
355 360 {
356 361 ssize_t len;
357 362
358 363 if (start + 1 == end) {
359 364 /* trivial case, output a decoded list */
360 365 return get_next_item(bins, start);
361 366 }
362 367
363 368 /* divide and conquer, memory management is elsewhere */
364 369 len = (end - start) / 2;
365 370 return combine(mpatch_fold(bins, get_next_item, start, start + len),
366 371 mpatch_fold(bins, get_next_item, start + len, end));
367 372 }
General Comments 0
You need to be logged in to leave comments. Login now