Fossil SCM
Add checks to ensure that Blob allocation sizes are within a legal max range, failing if they're too big, to address 'uv add' misbehavior reported in [forum:d5cd3e3c19|forum post d5cd3e3c19].
Commit
6efd41941ced78c5f34ea2c141c400f409ca0207f7a611b971eda31e96625438
Parent
91f0f00f5df1622…
1 file changed
+30
-12
+30
-12
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -175,10 +175,27 @@ | ||
| 175 | 175 | static void blob_panic(void){ |
| 176 | 176 | static const char zErrMsg[] = "out of memory\n"; |
| 177 | 177 | fputs(zErrMsg, stderr); |
| 178 | 178 | fossil_exit(1); |
| 179 | 179 | } |
| 180 | + | |
| 181 | +/* | |
| 182 | +** Maximum size of a Blob's managed memory. This is ~2GB, largely for | |
| 183 | +** historical reasons. | |
| 184 | +** | |
| 185 | +*/ | |
| 186 | +#define MAX_BLOB_SIZE 0x7fff000 | |
| 187 | + | |
| 188 | +/* | |
| 189 | +** If n >= MAX_BLOB_SIZE, calls blob_panic(), | |
| 190 | +** else this is a no-op. | |
| 191 | +*/ | |
| 192 | +static void blob_assert_safe_size(i64 n){ | |
| 193 | + if( n>=(i64)MAX_BLOB_SIZE ){ | |
| 194 | + blob_panic(); | |
| 195 | + } | |
| 196 | +} | |
| 180 | 197 | |
| 181 | 198 | /* |
| 182 | 199 | ** A reallocation function that assumes that aData came from malloc(). |
| 183 | 200 | ** This function attempts to resize the buffer of the blob to hold |
| 184 | 201 | ** newSize bytes. |
| @@ -194,11 +211,13 @@ | ||
| 194 | 211 | pBlob->nAlloc = 0; |
| 195 | 212 | pBlob->nUsed = 0; |
| 196 | 213 | pBlob->iCursor = 0; |
| 197 | 214 | pBlob->blobFlags = 0; |
| 198 | 215 | }else if( newSize>pBlob->nAlloc || newSize+4000<pBlob->nAlloc ){ |
| 199 | - char *pNew = fossil_realloc(pBlob->aData, newSize); | |
| 216 | + char *pNew; | |
| 217 | + blob_assert_safe_size((i64)newSize); | |
| 218 | + pNew = fossil_realloc(pBlob->aData, newSize); | |
| 200 | 219 | pBlob->aData = pNew; |
| 201 | 220 | pBlob->nAlloc = newSize; |
| 202 | 221 | if( pBlob->nUsed>pBlob->nAlloc ){ |
| 203 | 222 | pBlob->nUsed = pBlob->nAlloc; |
| 204 | 223 | } |
| @@ -219,11 +238,13 @@ | ||
| 219 | 238 | */ |
| 220 | 239 | static void blobReallocStatic(Blob *pBlob, unsigned int newSize){ |
| 221 | 240 | if( newSize==0 ){ |
| 222 | 241 | *pBlob = empty_blob; |
| 223 | 242 | }else{ |
| 224 | - char *pNew = fossil_malloc( newSize ); | |
| 243 | + char *pNew; | |
| 244 | + blob_assert_safe_size((i64)newSize); | |
| 245 | + pNew = fossil_malloc( newSize ); | |
| 225 | 246 | if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize; |
| 226 | 247 | memcpy(pNew, pBlob->aData, pBlob->nUsed); |
| 227 | 248 | pBlob->aData = pNew; |
| 228 | 249 | pBlob->xRealloc = blobReallocMalloc; |
| 229 | 250 | pBlob->nAlloc = newSize; |
| @@ -329,14 +350,12 @@ | ||
| 329 | 350 | nNew = pBlob->nUsed; |
| 330 | 351 | nNew += nData; |
| 331 | 352 | if( nNew >= pBlob->nAlloc ){ |
| 332 | 353 | nNew += pBlob->nAlloc; |
| 333 | 354 | nNew += 100; |
| 334 | - if( nNew>=0x7fff0000 ){ | |
| 335 | - blob_panic(); | |
| 336 | - } | |
| 337 | - pBlob->xRealloc(pBlob, (int)nNew); | |
| 355 | + blob_assert_safe_size(nNew); | |
| 356 | + pBlob->xRealloc(pBlob, (unsigned)nNew); | |
| 338 | 357 | if( pBlob->nUsed + nData >= pBlob->nAlloc ){ |
| 339 | 358 | blob_panic(); |
| 340 | 359 | } |
| 341 | 360 | } |
| 342 | 361 | memcpy(&pBlob->aData[pBlob->nUsed], aData, nData); |
| @@ -605,23 +624,22 @@ | ||
| 605 | 624 | ** Ensures that the given blob has at least the given amount of memory |
| 606 | 625 | ** allocated to it. Does not modify pBlob->nUsed nor will it reduce |
| 607 | 626 | ** the currently-allocated amount of memory. |
| 608 | 627 | ** |
| 609 | 628 | ** For semantic compatibility with blob_append_full(), if newSize is |
| 610 | -** >=0x7fff000 (~2GB) then this function will trigger blob_panic(). If | |
| 611 | -** it didn't, it would be possible to bypass that hard-coded limit via | |
| 629 | +** >=MAX_BLOB_SIZE then this function will trigger blob_panic(). If it | |
| 630 | +** didn't, it would be possible to bypass that hard-coded limit via | |
| 612 | 631 | ** this function. |
| 613 | 632 | ** |
| 614 | 633 | ** We've had at least one report: |
| 615 | 634 | ** https://fossil-scm.org/forum/forumpost/b7bbd28db4 |
| 616 | 635 | ** which implies that this is unconditionally failing on mingw 32-bit |
| 617 | 636 | ** builds. |
| 618 | 637 | */ |
| 619 | 638 | void blob_reserve(Blob *pBlob, unsigned int newSize){ |
| 620 | - if(newSize>=0x7fff0000 ){ | |
| 621 | - blob_panic(); | |
| 622 | - }else if(newSize>pBlob->nAlloc){ | |
| 639 | + blob_assert_safe_size( (i64)newSize ); | |
| 640 | + if(newSize>pBlob->nAlloc){ | |
| 623 | 641 | pBlob->xRealloc(pBlob, newSize+1); |
| 624 | 642 | pBlob->aData[newSize] = 0; |
| 625 | 643 | } |
| 626 | 644 | } |
| 627 | 645 | |
| @@ -919,11 +937,11 @@ | ||
| 919 | 937 | memset(&dCfg, 0, sizeof(dCfg)); |
| 920 | 938 | |
| 921 | 939 | sbs = find_option("side-by-side","y",0)!=0; |
| 922 | 940 | if( (z = find_option("width","W",1))!=0 && (w = atoi(z))>0 ){ |
| 923 | 941 | dCfg.wColumn = w; |
| 924 | - } | |
| 942 | + } | |
| 925 | 943 | verify_all_options(); |
| 926 | 944 | if( g.argc!=3 ) usage("INPUTFILE"); |
| 927 | 945 | |
| 928 | 946 | blob_read_from_file(&f, g.argv[2], ExtFILE); |
| 929 | 947 | blob_strip_comment_lines(&f, &h); |
| 930 | 948 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -175,10 +175,27 @@ | |
| 175 | static void blob_panic(void){ |
| 176 | static const char zErrMsg[] = "out of memory\n"; |
| 177 | fputs(zErrMsg, stderr); |
| 178 | fossil_exit(1); |
| 179 | } |
| 180 | |
| 181 | /* |
| 182 | ** A reallocation function that assumes that aData came from malloc(). |
| 183 | ** This function attempts to resize the buffer of the blob to hold |
| 184 | ** newSize bytes. |
| @@ -194,11 +211,13 @@ | |
| 194 | pBlob->nAlloc = 0; |
| 195 | pBlob->nUsed = 0; |
| 196 | pBlob->iCursor = 0; |
| 197 | pBlob->blobFlags = 0; |
| 198 | }else if( newSize>pBlob->nAlloc || newSize+4000<pBlob->nAlloc ){ |
| 199 | char *pNew = fossil_realloc(pBlob->aData, newSize); |
| 200 | pBlob->aData = pNew; |
| 201 | pBlob->nAlloc = newSize; |
| 202 | if( pBlob->nUsed>pBlob->nAlloc ){ |
| 203 | pBlob->nUsed = pBlob->nAlloc; |
| 204 | } |
| @@ -219,11 +238,13 @@ | |
| 219 | */ |
| 220 | static void blobReallocStatic(Blob *pBlob, unsigned int newSize){ |
| 221 | if( newSize==0 ){ |
| 222 | *pBlob = empty_blob; |
| 223 | }else{ |
| 224 | char *pNew = fossil_malloc( newSize ); |
| 225 | if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize; |
| 226 | memcpy(pNew, pBlob->aData, pBlob->nUsed); |
| 227 | pBlob->aData = pNew; |
| 228 | pBlob->xRealloc = blobReallocMalloc; |
| 229 | pBlob->nAlloc = newSize; |
| @@ -329,14 +350,12 @@ | |
| 329 | nNew = pBlob->nUsed; |
| 330 | nNew += nData; |
| 331 | if( nNew >= pBlob->nAlloc ){ |
| 332 | nNew += pBlob->nAlloc; |
| 333 | nNew += 100; |
| 334 | if( nNew>=0x7fff0000 ){ |
| 335 | blob_panic(); |
| 336 | } |
| 337 | pBlob->xRealloc(pBlob, (int)nNew); |
| 338 | if( pBlob->nUsed + nData >= pBlob->nAlloc ){ |
| 339 | blob_panic(); |
| 340 | } |
| 341 | } |
| 342 | memcpy(&pBlob->aData[pBlob->nUsed], aData, nData); |
| @@ -605,23 +624,22 @@ | |
| 605 | ** Ensures that the given blob has at least the given amount of memory |
| 606 | ** allocated to it. Does not modify pBlob->nUsed nor will it reduce |
| 607 | ** the currently-allocated amount of memory. |
| 608 | ** |
| 609 | ** For semantic compatibility with blob_append_full(), if newSize is |
| 610 | ** >=0x7fff000 (~2GB) then this function will trigger blob_panic(). If |
| 611 | ** it didn't, it would be possible to bypass that hard-coded limit via |
| 612 | ** this function. |
| 613 | ** |
| 614 | ** We've had at least one report: |
| 615 | ** https://fossil-scm.org/forum/forumpost/b7bbd28db4 |
| 616 | ** which implies that this is unconditionally failing on mingw 32-bit |
| 617 | ** builds. |
| 618 | */ |
| 619 | void blob_reserve(Blob *pBlob, unsigned int newSize){ |
| 620 | if(newSize>=0x7fff0000 ){ |
| 621 | blob_panic(); |
| 622 | }else if(newSize>pBlob->nAlloc){ |
| 623 | pBlob->xRealloc(pBlob, newSize+1); |
| 624 | pBlob->aData[newSize] = 0; |
| 625 | } |
| 626 | } |
| 627 | |
| @@ -919,11 +937,11 @@ | |
| 919 | memset(&dCfg, 0, sizeof(dCfg)); |
| 920 | |
| 921 | sbs = find_option("side-by-side","y",0)!=0; |
| 922 | if( (z = find_option("width","W",1))!=0 && (w = atoi(z))>0 ){ |
| 923 | dCfg.wColumn = w; |
| 924 | } |
| 925 | verify_all_options(); |
| 926 | if( g.argc!=3 ) usage("INPUTFILE"); |
| 927 | |
| 928 | blob_read_from_file(&f, g.argv[2], ExtFILE); |
| 929 | blob_strip_comment_lines(&f, &h); |
| 930 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -175,10 +175,27 @@ | |
| 175 | static void blob_panic(void){ |
| 176 | static const char zErrMsg[] = "out of memory\n"; |
| 177 | fputs(zErrMsg, stderr); |
| 178 | fossil_exit(1); |
| 179 | } |
| 180 | |
| 181 | /* |
| 182 | ** Maximum size of a Blob's managed memory. This is ~2GB, largely for |
| 183 | ** historical reasons. |
| 184 | ** |
| 185 | */ |
| 186 | #define MAX_BLOB_SIZE 0x7fff000 |
| 187 | |
| 188 | /* |
| 189 | ** If n >= MAX_BLOB_SIZE, calls blob_panic(), |
| 190 | ** else this is a no-op. |
| 191 | */ |
| 192 | static void blob_assert_safe_size(i64 n){ |
| 193 | if( n>=(i64)MAX_BLOB_SIZE ){ |
| 194 | blob_panic(); |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | /* |
| 199 | ** A reallocation function that assumes that aData came from malloc(). |
| 200 | ** This function attempts to resize the buffer of the blob to hold |
| 201 | ** newSize bytes. |
| @@ -194,11 +211,13 @@ | |
| 211 | pBlob->nAlloc = 0; |
| 212 | pBlob->nUsed = 0; |
| 213 | pBlob->iCursor = 0; |
| 214 | pBlob->blobFlags = 0; |
| 215 | }else if( newSize>pBlob->nAlloc || newSize+4000<pBlob->nAlloc ){ |
| 216 | char *pNew; |
| 217 | blob_assert_safe_size((i64)newSize); |
| 218 | pNew = fossil_realloc(pBlob->aData, newSize); |
| 219 | pBlob->aData = pNew; |
| 220 | pBlob->nAlloc = newSize; |
| 221 | if( pBlob->nUsed>pBlob->nAlloc ){ |
| 222 | pBlob->nUsed = pBlob->nAlloc; |
| 223 | } |
| @@ -219,11 +238,13 @@ | |
| 238 | */ |
| 239 | static void blobReallocStatic(Blob *pBlob, unsigned int newSize){ |
| 240 | if( newSize==0 ){ |
| 241 | *pBlob = empty_blob; |
| 242 | }else{ |
| 243 | char *pNew; |
| 244 | blob_assert_safe_size((i64)newSize); |
| 245 | pNew = fossil_malloc( newSize ); |
| 246 | if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize; |
| 247 | memcpy(pNew, pBlob->aData, pBlob->nUsed); |
| 248 | pBlob->aData = pNew; |
| 249 | pBlob->xRealloc = blobReallocMalloc; |
| 250 | pBlob->nAlloc = newSize; |
| @@ -329,14 +350,12 @@ | |
| 350 | nNew = pBlob->nUsed; |
| 351 | nNew += nData; |
| 352 | if( nNew >= pBlob->nAlloc ){ |
| 353 | nNew += pBlob->nAlloc; |
| 354 | nNew += 100; |
| 355 | blob_assert_safe_size(nNew); |
| 356 | pBlob->xRealloc(pBlob, (unsigned)nNew); |
| 357 | if( pBlob->nUsed + nData >= pBlob->nAlloc ){ |
| 358 | blob_panic(); |
| 359 | } |
| 360 | } |
| 361 | memcpy(&pBlob->aData[pBlob->nUsed], aData, nData); |
| @@ -605,23 +624,22 @@ | |
| 624 | ** Ensures that the given blob has at least the given amount of memory |
| 625 | ** allocated to it. Does not modify pBlob->nUsed nor will it reduce |
| 626 | ** the currently-allocated amount of memory. |
| 627 | ** |
| 628 | ** For semantic compatibility with blob_append_full(), if newSize is |
| 629 | ** >=MAX_BLOB_SIZE then this function will trigger blob_panic(). If it |
| 630 | ** didn't, it would be possible to bypass that hard-coded limit via |
| 631 | ** this function. |
| 632 | ** |
| 633 | ** We've had at least one report: |
| 634 | ** https://fossil-scm.org/forum/forumpost/b7bbd28db4 |
| 635 | ** which implies that this is unconditionally failing on mingw 32-bit |
| 636 | ** builds. |
| 637 | */ |
| 638 | void blob_reserve(Blob *pBlob, unsigned int newSize){ |
| 639 | blob_assert_safe_size( (i64)newSize ); |
| 640 | if(newSize>pBlob->nAlloc){ |
| 641 | pBlob->xRealloc(pBlob, newSize+1); |
| 642 | pBlob->aData[newSize] = 0; |
| 643 | } |
| 644 | } |
| 645 | |
| @@ -919,11 +937,11 @@ | |
| 937 | memset(&dCfg, 0, sizeof(dCfg)); |
| 938 | |
| 939 | sbs = find_option("side-by-side","y",0)!=0; |
| 940 | if( (z = find_option("width","W",1))!=0 && (w = atoi(z))>0 ){ |
| 941 | dCfg.wColumn = w; |
| 942 | } |
| 943 | verify_all_options(); |
| 944 | if( g.argc!=3 ) usage("INPUTFILE"); |
| 945 | |
| 946 | blob_read_from_file(&f, g.argv[2], ExtFILE); |
| 947 | blob_strip_comment_lines(&f, &h); |
| 948 |