Fossil SCM

Fix an integer overflow vulnerability. Shorten all source code lines to be no more than 80 characters. Do not select the "chunked" transfer encoding, if the last token of the transfer-encoding is some keyword that has "chunked" as a suffix, ex: "superchunked".

drh 2026-07-25 11:04 UTC http1-1-chunked
Commit 82723bf87c0da0c0a2fd214d5f13a24b4a0c38ca17933729c3118f334f7e035e
1 file changed +14 -11
+14 -11
--- src/http.c
+++ src/http.c
@@ -611,11 +611,12 @@
611611
iLength = atoi(&zLine[i]);
612612
}else if( fossil_strnicmp(zLine, "transfer-encoding:", 18)==0 ){
613613
/* RFC 7230: "chunked" must be the final transfer-coding so only
614614
** match when it appears at the end of the line. */
615615
if( sqlite3_strlike("%chunked", &zLine[18], 0)==0 ){
616
- isChunked = 1;
616
+ size_t nx = strlen(&zLine[18]);
617
+ if( !fossil_isalnum(zLine[nx+11]) ) isChunked = 1;
617618
}
618619
}else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
619620
if( sqlite3_strlike("%close%", &zLine[11], 0)==0 ){
620621
closeConnection = 1;
621622
}else if( sqlite3_strlike("%keep-alive%", &zLine[11], 0)==0 ){
@@ -750,15 +751,15 @@
750751
** chunk is a hex length on its own line (optionally followed by a
751752
** ";extension" that is ignored), then that many payload bytes, then a
752753
** bare CRLF. A zero-length chunk terminates the body, after which any
753754
** trailer header lines are read and discarded up to the blank line. */
754755
char *zChunk;
755
- int sawTerminator = 0; /* True once the 0-length chunk is seen */
756
+ int sawTerminator = 0; /* True once the 0-length chunk is seen */
756757
while( (zChunk = transport_receive_line(&g.url))!=0 && zChunk[0]!=0 ){
757
- sqlite3_int64 nChunk; /* Size of this chunk in bytes (wide, unclamped) */
758
- unsigned int nPrior; /* Bytes already in pReply (matches blob nUsed) */
759
- char *zEnd = 0; /* End of the hex digits actually parsed */
758
+ i64 nChunk; /* Size of this chunk in bytes (wide, unclamped) */
759
+ i64 nPrior; /* Bytes already in pReply (matches blob nUsed) */
760
+ char *zEnd = 0; /* End of the hex digits actually parsed */
760761
while( fossil_isspace(zChunk[0]) ) zChunk++;
761762
nChunk = strtoll(zChunk, &zEnd, 16);
762763
if( zEnd==zChunk ){
763764
/* No hex digit consumed: a blank or malformed chunk-size line, which
764765
** is the symptom of a connection that closed mid-stream. Treat it as
@@ -779,23 +780,25 @@
779780
}
780781
nPrior = blob_size(pReply);
781782
/* Reserve space without advancing nUsed, so that on error
782783
** the blob's reported size equals the bytes actually
783784
** received rather the claimed chunk length */
784
- blob_reserve(pReply, nPrior+(unsigned int)nChunk);
785
+ blob_reserve(pReply, (u64)(nPrior+nChunk));
785786
{
786787
unsigned int nRemaining = (unsigned int)nChunk;
787
- /* transport_receive() may return short; loop until the chunk is full. */
788
+ /* transport_receive() may return short; loop until the chunk is
789
+ ** full. */
788790
while( nRemaining>0 ){
789
- int nGot = transport_receive(&g.url, &pReply->aData[nPrior], nRemaining);
791
+ int nGot;
792
+ nGot = transport_receive(&g.url, &pReply->aData[nPrior], nRemaining);
790793
if( nGot<=0 ){
791794
fossil_warning("chunked reply truncated");
792795
goto write_err;
793796
}
794
- nPrior += (unsigned int)nGot;
795
- nRemaining -= (unsigned int)nGot;
796
- pReply->nUsed = nPrior;
797
+ nPrior += nGot;
798
+ nRemaining -= nGot;
799
+ pReply->nUsed = (unsigned int)nPrior;
797800
}
798801
}
799802
transport_receive_line(&g.url); /* CRLF that follows the chunk data */
800803
}
801804
if( !sawTerminator ){
802805
--- src/http.c
+++ src/http.c
@@ -611,11 +611,12 @@
611 iLength = atoi(&zLine[i]);
612 }else if( fossil_strnicmp(zLine, "transfer-encoding:", 18)==0 ){
613 /* RFC 7230: "chunked" must be the final transfer-coding so only
614 ** match when it appears at the end of the line. */
615 if( sqlite3_strlike("%chunked", &zLine[18], 0)==0 ){
616 isChunked = 1;
 
617 }
618 }else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
619 if( sqlite3_strlike("%close%", &zLine[11], 0)==0 ){
620 closeConnection = 1;
621 }else if( sqlite3_strlike("%keep-alive%", &zLine[11], 0)==0 ){
@@ -750,15 +751,15 @@
750 ** chunk is a hex length on its own line (optionally followed by a
751 ** ";extension" that is ignored), then that many payload bytes, then a
752 ** bare CRLF. A zero-length chunk terminates the body, after which any
753 ** trailer header lines are read and discarded up to the blank line. */
754 char *zChunk;
755 int sawTerminator = 0; /* True once the 0-length chunk is seen */
756 while( (zChunk = transport_receive_line(&g.url))!=0 && zChunk[0]!=0 ){
757 sqlite3_int64 nChunk; /* Size of this chunk in bytes (wide, unclamped) */
758 unsigned int nPrior; /* Bytes already in pReply (matches blob nUsed) */
759 char *zEnd = 0; /* End of the hex digits actually parsed */
760 while( fossil_isspace(zChunk[0]) ) zChunk++;
761 nChunk = strtoll(zChunk, &zEnd, 16);
762 if( zEnd==zChunk ){
763 /* No hex digit consumed: a blank or malformed chunk-size line, which
764 ** is the symptom of a connection that closed mid-stream. Treat it as
@@ -779,23 +780,25 @@
779 }
780 nPrior = blob_size(pReply);
781 /* Reserve space without advancing nUsed, so that on error
782 ** the blob's reported size equals the bytes actually
783 ** received rather the claimed chunk length */
784 blob_reserve(pReply, nPrior+(unsigned int)nChunk);
785 {
786 unsigned int nRemaining = (unsigned int)nChunk;
787 /* transport_receive() may return short; loop until the chunk is full. */
 
788 while( nRemaining>0 ){
789 int nGot = transport_receive(&g.url, &pReply->aData[nPrior], nRemaining);
 
790 if( nGot<=0 ){
791 fossil_warning("chunked reply truncated");
792 goto write_err;
793 }
794 nPrior += (unsigned int)nGot;
795 nRemaining -= (unsigned int)nGot;
796 pReply->nUsed = nPrior;
797 }
798 }
799 transport_receive_line(&g.url); /* CRLF that follows the chunk data */
800 }
801 if( !sawTerminator ){
802
--- src/http.c
+++ src/http.c
@@ -611,11 +611,12 @@
611 iLength = atoi(&zLine[i]);
612 }else if( fossil_strnicmp(zLine, "transfer-encoding:", 18)==0 ){
613 /* RFC 7230: "chunked" must be the final transfer-coding so only
614 ** match when it appears at the end of the line. */
615 if( sqlite3_strlike("%chunked", &zLine[18], 0)==0 ){
616 size_t nx = strlen(&zLine[18]);
617 if( !fossil_isalnum(zLine[nx+11]) ) isChunked = 1;
618 }
619 }else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
620 if( sqlite3_strlike("%close%", &zLine[11], 0)==0 ){
621 closeConnection = 1;
622 }else if( sqlite3_strlike("%keep-alive%", &zLine[11], 0)==0 ){
@@ -750,15 +751,15 @@
751 ** chunk is a hex length on its own line (optionally followed by a
752 ** ";extension" that is ignored), then that many payload bytes, then a
753 ** bare CRLF. A zero-length chunk terminates the body, after which any
754 ** trailer header lines are read and discarded up to the blank line. */
755 char *zChunk;
756 int sawTerminator = 0; /* True once the 0-length chunk is seen */
757 while( (zChunk = transport_receive_line(&g.url))!=0 && zChunk[0]!=0 ){
758 i64 nChunk; /* Size of this chunk in bytes (wide, unclamped) */
759 i64 nPrior; /* Bytes already in pReply (matches blob nUsed) */
760 char *zEnd = 0; /* End of the hex digits actually parsed */
761 while( fossil_isspace(zChunk[0]) ) zChunk++;
762 nChunk = strtoll(zChunk, &zEnd, 16);
763 if( zEnd==zChunk ){
764 /* No hex digit consumed: a blank or malformed chunk-size line, which
765 ** is the symptom of a connection that closed mid-stream. Treat it as
@@ -779,23 +780,25 @@
780 }
781 nPrior = blob_size(pReply);
782 /* Reserve space without advancing nUsed, so that on error
783 ** the blob's reported size equals the bytes actually
784 ** received rather the claimed chunk length */
785 blob_reserve(pReply, (u64)(nPrior+nChunk));
786 {
787 unsigned int nRemaining = (unsigned int)nChunk;
788 /* transport_receive() may return short; loop until the chunk is
789 ** full. */
790 while( nRemaining>0 ){
791 int nGot;
792 nGot = transport_receive(&g.url, &pReply->aData[nPrior], nRemaining);
793 if( nGot<=0 ){
794 fossil_warning("chunked reply truncated");
795 goto write_err;
796 }
797 nPrior += nGot;
798 nRemaining -= nGot;
799 pReply->nUsed = (unsigned int)nPrior;
800 }
801 }
802 transport_receive_line(&g.url); /* CRLF that follows the chunk data */
803 }
804 if( !sawTerminator ){
805

Keyboard Shortcuts

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