Fossil SCM

Harden the Blob object against integer overflow attacks.

drh 2026-07-25 10:04 UTC trunk
Commit 4816e03320c664758e46b0bcca2a2e309e06ce9e33960f62090a717e7111dd9e
1 file changed +17 -18
+17 -18
--- src/blob.c
+++ src/blob.c
@@ -35,11 +35,11 @@
3535
unsigned int nUsed; /* Number of bytes used in aData[] */
3636
unsigned int nAlloc; /* Number of bytes allocated for aData[] */
3737
unsigned int iCursor; /* Next character of input to parse */
3838
unsigned int blobFlags; /* One or more BLOBFLAG_* bits */
3939
char *aData; /* Where the information is stored */
40
- void (*xRealloc)(Blob*, unsigned int); /* Function to reallocate the buffer */
40
+ void (*xRealloc)(Blob*,u64); /* Function to reallocate the buffer */
4141
};
4242
4343
/*
4444
** Allowed values for Blob.blobFlags
4545
*/
@@ -190,11 +190,11 @@
190190
191191
/*
192192
** If n >= MAX_BLOB_SIZE, calls blob_panic(),
193193
** else this is a no-op.
194194
*/
195
-static void blob_assert_safe_size(i64 n){
195
+static void blob_assert_safe_size(u64 n){
196196
if( n>=(i64)MAX_BLOB_SIZE ){
197197
blob_panic();
198198
}
199199
}
200200
@@ -205,24 +205,24 @@
205205
**
206206
** No attempt is made to recover from an out-of-memory error.
207207
** If an OOM error occurs, an error message is printed on stderr
208208
** and the program exits.
209209
*/
210
-void blobReallocMalloc(Blob *pBlob, unsigned int newSize){
210
+void blobReallocMalloc(Blob *pBlob, u64 newSize){
211211
if( newSize==0 ){
212212
free(pBlob->aData);
213213
pBlob->aData = 0;
214214
pBlob->nAlloc = 0;
215215
pBlob->nUsed = 0;
216216
pBlob->iCursor = 0;
217217
pBlob->blobFlags = 0;
218218
}else if( newSize>pBlob->nAlloc || newSize+4000<pBlob->nAlloc ){
219219
char *pNew;
220
- blob_assert_safe_size((i64)newSize);
220
+ blob_assert_safe_size(newSize);
221221
pNew = fossil_realloc(pBlob->aData, newSize);
222222
pBlob->aData = pNew;
223
- pBlob->nAlloc = newSize;
223
+ pBlob->nAlloc = (unsigned int)newSize;
224224
if( pBlob->nUsed>pBlob->nAlloc ){
225225
pBlob->nUsed = pBlob->nAlloc;
226226
}
227227
}
228228
}
@@ -237,22 +237,22 @@
237237
238238
/*
239239
** A reallocation function for when the initial string is in unmanaged
240240
** space. Copy the string to memory obtained from malloc().
241241
*/
242
-static void blobReallocStatic(Blob *pBlob, unsigned int newSize){
242
+static void blobReallocStatic(Blob *pBlob, u64 newSize){
243243
if( newSize==0 ){
244244
*pBlob = empty_blob;
245245
}else{
246246
char *pNew;
247
- blob_assert_safe_size((i64)newSize);
247
+ blob_assert_safe_size(newSize);
248248
pNew = fossil_malloc( newSize );
249249
if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize;
250250
memcpy(pNew, pBlob->aData, pBlob->nUsed);
251251
pBlob->aData = pNew;
252252
pBlob->xRealloc = blobReallocMalloc;
253
- pBlob->nAlloc = newSize;
253
+ pBlob->nAlloc = (unsigned int)newSize;
254254
}
255255
}
256256
257257
/*
258258
** Reset a blob to be an empty container.
@@ -335,11 +335,11 @@
335335
** routine is faster, but blob_append_full() handles all the corner cases.
336336
** The blob_append() routine automatically calls blob_append_full() if
337337
** necessary.
338338
*/
339339
static void blob_append_full(Blob *pBlob, const char *aData, int nData){
340
- sqlite3_int64 nNew;
340
+ u64 nNew;
341341
/* assert( aData!=0 || nData==0 ); // omitted for speed */
342342
/* blob_is_init(pBlob); // omitted for speed */
343343
if( nData<0 ) nData = strlen(aData);
344344
if( nData==0 ) return;
345345
if( pBlob==0 ){
@@ -364,11 +364,11 @@
364364
memcpy(&pBlob->aData[pBlob->nUsed], aData, nData);
365365
pBlob->nUsed += nData;
366366
pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */
367367
}
368368
void blob_append(Blob *pBlob, const char *aData, int nData){
369
- sqlite3_int64 nUsed;
369
+ u64 nUsed;
370370
/* assert( aData!=0 || nData==0 ); // omitted for speed */
371371
if( nData<=0 || pBlob==0 || pBlob->nUsed + nData >= pBlob->nAlloc ){
372372
blob_append_full(pBlob, aData, nData);
373373
return;
374374
}
@@ -414,12 +414,12 @@
414414
** Write into pOut, a string literal representation for the first n bytes
415415
** of z[]. The string literal representation is compatible with C, TCL,
416416
** and JSON. Double-quotes are added to both ends. Double-quote and
417417
** backslash characters are escaped.
418418
*/
419
-void blob_append_tcl_literal(Blob *pOut, const char *z, int n){
420
- int i;
419
+void blob_append_tcl_literal(Blob *pOut, const char *z, size_t n){
420
+ size_t i;
421421
blob_append_char(pOut, '"');
422422
for(i=0; i<n; i++){
423423
char c = z[i];
424424
switch( c ){
425425
case '\r': c = 'r';
@@ -433,12 +433,12 @@
433433
blob_append_char(pOut, c);
434434
}
435435
}
436436
blob_append_char(pOut, '"');
437437
}
438
-void blob_append_json_literal(Blob *pOut, const char *z, int n){
439
- int i;
438
+void blob_append_json_literal(Blob *pOut, const char *z, size_t n){
439
+ size_t i;
440440
blob_append_char(pOut, '"');
441441
for(i=0; i<n; i++){
442442
char c = z[i];
443443
switch( c ){
444444
case 0x00:
@@ -615,11 +615,11 @@
615615
616616
/*
617617
** Attempt to resize a blob so that its internal buffer is
618618
** nByte in size. The blob is truncated if necessary.
619619
*/
620
-void blob_resize(Blob *pBlob, unsigned int newSize){
620
+void blob_resize(Blob *pBlob, u64 newSize){
621621
pBlob->xRealloc(pBlob, newSize+1);
622622
pBlob->nUsed = newSize;
623623
pBlob->aData[newSize] = 0;
624624
}
625625
@@ -636,12 +636,12 @@
636636
** We've had at least one report:
637637
** https://fossil-scm.org/forum/forumpost/b7bbd28db4
638638
** which implies that this is unconditionally failing on mingw 32-bit
639639
** builds.
640640
*/
641
-void blob_reserve(Blob *pBlob, unsigned int newSize){
642
- blob_assert_safe_size( (i64)newSize );
641
+void blob_reserve(Blob *pBlob, u64 newSize){
642
+ blob_assert_safe_size( newSize );
643643
if(newSize>pBlob->nAlloc){
644644
pBlob->xRealloc(pBlob, newSize+1);
645645
pBlob->aData[newSize] = 0;
646646
}
647647
}
@@ -652,11 +652,10 @@
652652
*/
653653
char *blob_materialize(Blob *pBlob){
654654
blob_resize(pBlob, pBlob->nUsed);
655655
return pBlob->aData;
656656
}
657
-
658657
659658
/*
660659
** Call dehttpize on a blob. This causes an ephemeral blob to be
661660
** materialized.
662661
*/
663662
--- src/blob.c
+++ src/blob.c
@@ -35,11 +35,11 @@
35 unsigned int nUsed; /* Number of bytes used in aData[] */
36 unsigned int nAlloc; /* Number of bytes allocated for aData[] */
37 unsigned int iCursor; /* Next character of input to parse */
38 unsigned int blobFlags; /* One or more BLOBFLAG_* bits */
39 char *aData; /* Where the information is stored */
40 void (*xRealloc)(Blob*, unsigned int); /* Function to reallocate the buffer */
41 };
42
43 /*
44 ** Allowed values for Blob.blobFlags
45 */
@@ -190,11 +190,11 @@
190
191 /*
192 ** If n >= MAX_BLOB_SIZE, calls blob_panic(),
193 ** else this is a no-op.
194 */
195 static void blob_assert_safe_size(i64 n){
196 if( n>=(i64)MAX_BLOB_SIZE ){
197 blob_panic();
198 }
199 }
200
@@ -205,24 +205,24 @@
205 **
206 ** No attempt is made to recover from an out-of-memory error.
207 ** If an OOM error occurs, an error message is printed on stderr
208 ** and the program exits.
209 */
210 void blobReallocMalloc(Blob *pBlob, unsigned int newSize){
211 if( newSize==0 ){
212 free(pBlob->aData);
213 pBlob->aData = 0;
214 pBlob->nAlloc = 0;
215 pBlob->nUsed = 0;
216 pBlob->iCursor = 0;
217 pBlob->blobFlags = 0;
218 }else if( newSize>pBlob->nAlloc || newSize+4000<pBlob->nAlloc ){
219 char *pNew;
220 blob_assert_safe_size((i64)newSize);
221 pNew = fossil_realloc(pBlob->aData, newSize);
222 pBlob->aData = pNew;
223 pBlob->nAlloc = newSize;
224 if( pBlob->nUsed>pBlob->nAlloc ){
225 pBlob->nUsed = pBlob->nAlloc;
226 }
227 }
228 }
@@ -237,22 +237,22 @@
237
238 /*
239 ** A reallocation function for when the initial string is in unmanaged
240 ** space. Copy the string to memory obtained from malloc().
241 */
242 static void blobReallocStatic(Blob *pBlob, unsigned int newSize){
243 if( newSize==0 ){
244 *pBlob = empty_blob;
245 }else{
246 char *pNew;
247 blob_assert_safe_size((i64)newSize);
248 pNew = fossil_malloc( newSize );
249 if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize;
250 memcpy(pNew, pBlob->aData, pBlob->nUsed);
251 pBlob->aData = pNew;
252 pBlob->xRealloc = blobReallocMalloc;
253 pBlob->nAlloc = newSize;
254 }
255 }
256
257 /*
258 ** Reset a blob to be an empty container.
@@ -335,11 +335,11 @@
335 ** routine is faster, but blob_append_full() handles all the corner cases.
336 ** The blob_append() routine automatically calls blob_append_full() if
337 ** necessary.
338 */
339 static void blob_append_full(Blob *pBlob, const char *aData, int nData){
340 sqlite3_int64 nNew;
341 /* assert( aData!=0 || nData==0 ); // omitted for speed */
342 /* blob_is_init(pBlob); // omitted for speed */
343 if( nData<0 ) nData = strlen(aData);
344 if( nData==0 ) return;
345 if( pBlob==0 ){
@@ -364,11 +364,11 @@
364 memcpy(&pBlob->aData[pBlob->nUsed], aData, nData);
365 pBlob->nUsed += nData;
366 pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */
367 }
368 void blob_append(Blob *pBlob, const char *aData, int nData){
369 sqlite3_int64 nUsed;
370 /* assert( aData!=0 || nData==0 ); // omitted for speed */
371 if( nData<=0 || pBlob==0 || pBlob->nUsed + nData >= pBlob->nAlloc ){
372 blob_append_full(pBlob, aData, nData);
373 return;
374 }
@@ -414,12 +414,12 @@
414 ** Write into pOut, a string literal representation for the first n bytes
415 ** of z[]. The string literal representation is compatible with C, TCL,
416 ** and JSON. Double-quotes are added to both ends. Double-quote and
417 ** backslash characters are escaped.
418 */
419 void blob_append_tcl_literal(Blob *pOut, const char *z, int n){
420 int i;
421 blob_append_char(pOut, '"');
422 for(i=0; i<n; i++){
423 char c = z[i];
424 switch( c ){
425 case '\r': c = 'r';
@@ -433,12 +433,12 @@
433 blob_append_char(pOut, c);
434 }
435 }
436 blob_append_char(pOut, '"');
437 }
438 void blob_append_json_literal(Blob *pOut, const char *z, int n){
439 int i;
440 blob_append_char(pOut, '"');
441 for(i=0; i<n; i++){
442 char c = z[i];
443 switch( c ){
444 case 0x00:
@@ -615,11 +615,11 @@
615
616 /*
617 ** Attempt to resize a blob so that its internal buffer is
618 ** nByte in size. The blob is truncated if necessary.
619 */
620 void blob_resize(Blob *pBlob, unsigned int newSize){
621 pBlob->xRealloc(pBlob, newSize+1);
622 pBlob->nUsed = newSize;
623 pBlob->aData[newSize] = 0;
624 }
625
@@ -636,12 +636,12 @@
636 ** We've had at least one report:
637 ** https://fossil-scm.org/forum/forumpost/b7bbd28db4
638 ** which implies that this is unconditionally failing on mingw 32-bit
639 ** builds.
640 */
641 void blob_reserve(Blob *pBlob, unsigned int newSize){
642 blob_assert_safe_size( (i64)newSize );
643 if(newSize>pBlob->nAlloc){
644 pBlob->xRealloc(pBlob, newSize+1);
645 pBlob->aData[newSize] = 0;
646 }
647 }
@@ -652,11 +652,10 @@
652 */
653 char *blob_materialize(Blob *pBlob){
654 blob_resize(pBlob, pBlob->nUsed);
655 return pBlob->aData;
656 }
657
658
659 /*
660 ** Call dehttpize on a blob. This causes an ephemeral blob to be
661 ** materialized.
662 */
663
--- src/blob.c
+++ src/blob.c
@@ -35,11 +35,11 @@
35 unsigned int nUsed; /* Number of bytes used in aData[] */
36 unsigned int nAlloc; /* Number of bytes allocated for aData[] */
37 unsigned int iCursor; /* Next character of input to parse */
38 unsigned int blobFlags; /* One or more BLOBFLAG_* bits */
39 char *aData; /* Where the information is stored */
40 void (*xRealloc)(Blob*,u64); /* Function to reallocate the buffer */
41 };
42
43 /*
44 ** Allowed values for Blob.blobFlags
45 */
@@ -190,11 +190,11 @@
190
191 /*
192 ** If n >= MAX_BLOB_SIZE, calls blob_panic(),
193 ** else this is a no-op.
194 */
195 static void blob_assert_safe_size(u64 n){
196 if( n>=(i64)MAX_BLOB_SIZE ){
197 blob_panic();
198 }
199 }
200
@@ -205,24 +205,24 @@
205 **
206 ** No attempt is made to recover from an out-of-memory error.
207 ** If an OOM error occurs, an error message is printed on stderr
208 ** and the program exits.
209 */
210 void blobReallocMalloc(Blob *pBlob, u64 newSize){
211 if( newSize==0 ){
212 free(pBlob->aData);
213 pBlob->aData = 0;
214 pBlob->nAlloc = 0;
215 pBlob->nUsed = 0;
216 pBlob->iCursor = 0;
217 pBlob->blobFlags = 0;
218 }else if( newSize>pBlob->nAlloc || newSize+4000<pBlob->nAlloc ){
219 char *pNew;
220 blob_assert_safe_size(newSize);
221 pNew = fossil_realloc(pBlob->aData, newSize);
222 pBlob->aData = pNew;
223 pBlob->nAlloc = (unsigned int)newSize;
224 if( pBlob->nUsed>pBlob->nAlloc ){
225 pBlob->nUsed = pBlob->nAlloc;
226 }
227 }
228 }
@@ -237,22 +237,22 @@
237
238 /*
239 ** A reallocation function for when the initial string is in unmanaged
240 ** space. Copy the string to memory obtained from malloc().
241 */
242 static void blobReallocStatic(Blob *pBlob, u64 newSize){
243 if( newSize==0 ){
244 *pBlob = empty_blob;
245 }else{
246 char *pNew;
247 blob_assert_safe_size(newSize);
248 pNew = fossil_malloc( newSize );
249 if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize;
250 memcpy(pNew, pBlob->aData, pBlob->nUsed);
251 pBlob->aData = pNew;
252 pBlob->xRealloc = blobReallocMalloc;
253 pBlob->nAlloc = (unsigned int)newSize;
254 }
255 }
256
257 /*
258 ** Reset a blob to be an empty container.
@@ -335,11 +335,11 @@
335 ** routine is faster, but blob_append_full() handles all the corner cases.
336 ** The blob_append() routine automatically calls blob_append_full() if
337 ** necessary.
338 */
339 static void blob_append_full(Blob *pBlob, const char *aData, int nData){
340 u64 nNew;
341 /* assert( aData!=0 || nData==0 ); // omitted for speed */
342 /* blob_is_init(pBlob); // omitted for speed */
343 if( nData<0 ) nData = strlen(aData);
344 if( nData==0 ) return;
345 if( pBlob==0 ){
@@ -364,11 +364,11 @@
364 memcpy(&pBlob->aData[pBlob->nUsed], aData, nData);
365 pBlob->nUsed += nData;
366 pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */
367 }
368 void blob_append(Blob *pBlob, const char *aData, int nData){
369 u64 nUsed;
370 /* assert( aData!=0 || nData==0 ); // omitted for speed */
371 if( nData<=0 || pBlob==0 || pBlob->nUsed + nData >= pBlob->nAlloc ){
372 blob_append_full(pBlob, aData, nData);
373 return;
374 }
@@ -414,12 +414,12 @@
414 ** Write into pOut, a string literal representation for the first n bytes
415 ** of z[]. The string literal representation is compatible with C, TCL,
416 ** and JSON. Double-quotes are added to both ends. Double-quote and
417 ** backslash characters are escaped.
418 */
419 void blob_append_tcl_literal(Blob *pOut, const char *z, size_t n){
420 size_t i;
421 blob_append_char(pOut, '"');
422 for(i=0; i<n; i++){
423 char c = z[i];
424 switch( c ){
425 case '\r': c = 'r';
@@ -433,12 +433,12 @@
433 blob_append_char(pOut, c);
434 }
435 }
436 blob_append_char(pOut, '"');
437 }
438 void blob_append_json_literal(Blob *pOut, const char *z, size_t n){
439 size_t i;
440 blob_append_char(pOut, '"');
441 for(i=0; i<n; i++){
442 char c = z[i];
443 switch( c ){
444 case 0x00:
@@ -615,11 +615,11 @@
615
616 /*
617 ** Attempt to resize a blob so that its internal buffer is
618 ** nByte in size. The blob is truncated if necessary.
619 */
620 void blob_resize(Blob *pBlob, u64 newSize){
621 pBlob->xRealloc(pBlob, newSize+1);
622 pBlob->nUsed = newSize;
623 pBlob->aData[newSize] = 0;
624 }
625
@@ -636,12 +636,12 @@
636 ** We've had at least one report:
637 ** https://fossil-scm.org/forum/forumpost/b7bbd28db4
638 ** which implies that this is unconditionally failing on mingw 32-bit
639 ** builds.
640 */
641 void blob_reserve(Blob *pBlob, u64 newSize){
642 blob_assert_safe_size( newSize );
643 if(newSize>pBlob->nAlloc){
644 pBlob->xRealloc(pBlob, newSize+1);
645 pBlob->aData[newSize] = 0;
646 }
647 }
@@ -652,11 +652,10 @@
652 */
653 char *blob_materialize(Blob *pBlob){
654 blob_resize(pBlob, pBlob->nUsed);
655 return pBlob->aData;
656 }
 
657
658 /*
659 ** Call dehttpize on a blob. This causes an ephemeral blob to be
660 ** materialized.
661 */
662

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button