##// END OF EJS Templates
mpatch: introduce a safeadd() helper to work around UB int overflow...
Augie Fackler -
r38248:1ec4cb8c stable
parent child Browse files
Show More
@@ -1,285 +1,313 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 #include <limits.h>
23 24 #include <stdlib.h>
24 25 #include <string.h>
25 26
26 27 #include "bitmanipulation.h"
27 28 #include "compat.h"
28 29 #include "mpatch.h"
29 30
31 /* VC9 doesn't include bool and lacks stdbool.h based on cext/util.h */
32 #if defined(_MSC_VER) || __STDC_VERSION__ < 199901L
33 #define true 1
34 #define false 0
35 typedef unsigned char bool;
36 #else
37 #include <stdbool.h>
38 #endif
39
30 40 static struct mpatch_flist *lalloc(ssize_t size)
31 41 {
32 42 struct mpatch_flist *a = NULL;
33 43
34 44 if (size < 1)
35 45 size = 1;
36 46
37 47 a = (struct mpatch_flist *)malloc(sizeof(struct mpatch_flist));
38 48 if (a) {
39 49 a->base = (struct mpatch_frag *)malloc(
40 50 sizeof(struct mpatch_frag) * size);
41 51 if (a->base) {
42 52 a->head = a->tail = a->base;
43 53 return a;
44 54 }
45 55 free(a);
46 56 }
47 57 return NULL;
48 58 }
49 59
50 60 void mpatch_lfree(struct mpatch_flist *a)
51 61 {
52 62 if (a) {
53 63 free(a->base);
54 64 free(a);
55 65 }
56 66 }
57 67
58 68 static ssize_t lsize(struct mpatch_flist *a)
59 69 {
60 70 return a->tail - a->head;
61 71 }
62 72
73 /* add helper to add src and *dest iff it won't overflow */
74 static inline bool safeadd(int src, int *dest)
75 {
76 if ((src > 0) == (*dest > 0)) {
77 if (*dest > 0) {
78 if (src > (INT_MAX - *dest)) {
79 return false;
80 }
81 } else {
82 if (src < (INT_MIN - *dest)) {
83 return false;
84 }
85 }
86 }
87 *dest += src;
88 return true;
89 }
90
63 91 /* move hunks in source that are less cut to dest, compensating
64 92 for changes in offset. the last hunk may be split if necessary.
65 93 */
66 94 static int gather(struct mpatch_flist *dest, struct mpatch_flist *src, int cut,
67 95 int offset)
68 96 {
69 97 struct mpatch_frag *d = dest->tail, *s = src->head;
70 98 int postend, c, l;
71 99
72 100 while (s != src->tail) {
73 101 if (s->start + offset >= cut)
74 102 break; /* we've gone far enough */
75 103
76 104 postend = offset + s->start + s->len;
77 105 if (postend <= cut) {
78 106 /* save this hunk */
79 107 offset += s->start + s->len - s->end;
80 108 *d++ = *s++;
81 109 } else {
82 110 /* break up this hunk */
83 111 c = cut - offset;
84 112 if (s->end < c)
85 113 c = s->end;
86 114 l = cut - offset - s->start;
87 115 if (s->len < l)
88 116 l = s->len;
89 117
90 118 offset += s->start + l - c;
91 119
92 120 d->start = s->start;
93 121 d->end = c;
94 122 d->len = l;
95 123 d->data = s->data;
96 124 d++;
97 125 s->start = c;
98 126 s->len = s->len - l;
99 127 s->data = s->data + l;
100 128
101 129 break;
102 130 }
103 131 }
104 132
105 133 dest->tail = d;
106 134 src->head = s;
107 135 return offset;
108 136 }
109 137
110 138 /* like gather, but with no output list */
111 139 static int discard(struct mpatch_flist *src, int cut, int offset)
112 140 {
113 141 struct mpatch_frag *s = src->head;
114 142 int postend, c, l;
115 143
116 144 while (s != src->tail) {
117 145 if (s->start + offset >= cut)
118 146 break;
119 147
120 148 postend = offset + s->start + s->len;
121 149 if (postend <= cut) {
122 150 offset += s->start + s->len - s->end;
123 151 s++;
124 152 } else {
125 153 c = cut - offset;
126 154 if (s->end < c)
127 155 c = s->end;
128 156 l = cut - offset - s->start;
129 157 if (s->len < l)
130 158 l = s->len;
131 159
132 160 offset += s->start + l - c;
133 161 s->start = c;
134 162 s->len = s->len - l;
135 163 s->data = s->data + l;
136 164
137 165 break;
138 166 }
139 167 }
140 168
141 169 src->head = s;
142 170 return offset;
143 171 }
144 172
145 173 /* combine hunk lists a and b, while adjusting b for offset changes in a/
146 174 this deletes a and b and returns the resultant list. */
147 175 static struct mpatch_flist *combine(struct mpatch_flist *a,
148 176 struct mpatch_flist *b)
149 177 {
150 178 struct mpatch_flist *c = NULL;
151 179 struct mpatch_frag *bh, *ct;
152 180 int offset = 0, post;
153 181
154 182 if (a && b)
155 183 c = lalloc((lsize(a) + lsize(b)) * 2);
156 184
157 185 if (c) {
158 186
159 187 for (bh = b->head; bh != b->tail; bh++) {
160 188 /* save old hunks */
161 189 offset = gather(c, a, bh->start, offset);
162 190
163 191 /* discard replaced hunks */
164 192 post = discard(a, bh->end, offset);
165 193
166 194 /* insert new hunk */
167 195 ct = c->tail;
168 196 ct->start = bh->start - offset;
169 197 ct->end = bh->end - post;
170 198 ct->len = bh->len;
171 199 ct->data = bh->data;
172 200 c->tail++;
173 201 offset = post;
174 202 }
175 203
176 204 /* hold on to tail from a */
177 205 memcpy(c->tail, a->head, sizeof(struct mpatch_frag) * lsize(a));
178 206 c->tail += lsize(a);
179 207 }
180 208
181 209 mpatch_lfree(a);
182 210 mpatch_lfree(b);
183 211 return c;
184 212 }
185 213
186 214 /* decode a binary patch into a hunk list */
187 215 int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist **res)
188 216 {
189 217 struct mpatch_flist *l;
190 218 struct mpatch_frag *lt;
191 219 int pos = 0;
192 220
193 221 /* assume worst case size, we won't have many of these lists */
194 222 l = lalloc(len / 12 + 1);
195 223 if (!l)
196 224 return MPATCH_ERR_NO_MEM;
197 225
198 226 lt = l->tail;
199 227
200 228 /* We check against len-11 to ensure we have at least 12 bytes
201 229 left in the patch so we can read our three be32s out of it. */
202 230 while (pos >= 0 && pos < (len - 11)) {
203 231 lt->start = getbe32(bin + pos);
204 232 lt->end = getbe32(bin + pos + 4);
205 233 lt->len = getbe32(bin + pos + 8);
206 234 lt->data = bin + pos + 12;
207 235 pos += 12 + lt->len;
208 236 if (lt->start > lt->end || lt->len < 0)
209 237 break; /* sanity check */
210 238 lt++;
211 239 }
212 240
213 241 if (pos != len) {
214 242 mpatch_lfree(l);
215 243 return MPATCH_ERR_CANNOT_BE_DECODED;
216 244 }
217 245
218 246 l->tail = lt;
219 247 *res = l;
220 248 return 0;
221 249 }
222 250
223 251 /* calculate the size of resultant text */
224 252 ssize_t mpatch_calcsize(ssize_t len, struct mpatch_flist *l)
225 253 {
226 254 ssize_t outlen = 0, last = 0;
227 255 struct mpatch_frag *f = l->head;
228 256
229 257 while (f != l->tail) {
230 258 if (f->start < last || f->end > len) {
231 259 return MPATCH_ERR_INVALID_PATCH;
232 260 }
233 261 outlen += f->start - last;
234 262 last = f->end;
235 263 outlen += f->len;
236 264 f++;
237 265 }
238 266
239 267 outlen += len - last;
240 268 return outlen;
241 269 }
242 270
243 271 int mpatch_apply(char *buf, const char *orig, ssize_t len,
244 272 struct mpatch_flist *l)
245 273 {
246 274 struct mpatch_frag *f = l->head;
247 275 int last = 0;
248 276 char *p = buf;
249 277
250 278 while (f != l->tail) {
251 279 if (f->start < last || f->start > len || f->end > len ||
252 280 last < 0) {
253 281 return MPATCH_ERR_INVALID_PATCH;
254 282 }
255 283 memcpy(p, orig + last, f->start - last);
256 284 p += f->start - last;
257 285 memcpy(p, f->data, f->len);
258 286 last = f->end;
259 287 p += f->len;
260 288 f++;
261 289 }
262 290 if (last < 0) {
263 291 return MPATCH_ERR_INVALID_PATCH;
264 292 }
265 293 memcpy(p, orig + last, len - last);
266 294 return 0;
267 295 }
268 296
269 297 /* recursively generate a patch of all bins between start and end */
270 298 struct mpatch_flist *
271 299 mpatch_fold(void *bins, struct mpatch_flist *(*get_next_item)(void *, ssize_t),
272 300 ssize_t start, ssize_t end)
273 301 {
274 302 ssize_t len;
275 303
276 304 if (start + 1 == end) {
277 305 /* trivial case, output a decoded list */
278 306 return get_next_item(bins, start);
279 307 }
280 308
281 309 /* divide and conquer, memory management is elsewhere */
282 310 len = (end - start) / 2;
283 311 return combine(mpatch_fold(bins, get_next_item, start, start + len),
284 312 mpatch_fold(bins, get_next_item, start + len, end));
285 313 }
General Comments 0
You need to be logged in to leave comments. Login now