Procházet zdrojové kódy

Fix endless loop in apt-get update that can cause disk fillup

The apt http code parses Content-Length and Content-Range. For
both requests the variable "Size" is used and the semantic for
this Size is the total file size. However Content-Length is not
the entire file size for partital file requests. For servers that
send the Content-Range header first and then the Content-Length
header this can lead to globbing of Size so that its less than
the real file size. This may lead to a subsequent passing of a
negative number into the CircleBuf which leads to a endless
loop that writes data.

Thanks to Anton Blanchard for the analysis and initial patch.

LP: #1445239
Michael Vogt před 11 roky
rodič
revize
ceafe8a6ed
4 změnil soubory, kde provedl 21 přidání a 10 odebrání
  1. 1 1
      methods/http.cc
  2. 15 5
      methods/server.cc
  3. 2 1
      methods/server.h
  4. 3 3
      test/interactive-helper/aptwebserver.cc

+ 1 - 1
methods/http.cc

@@ -443,7 +443,7 @@ bool HttpServerState::RunData(FileFd * const File)
       else if (JunkSize != 0)
       else if (JunkSize != 0)
 	 In.Limit(JunkSize);
 	 In.Limit(JunkSize);
       else
       else
-	 In.Limit(Size - StartPos);
+	 In.Limit(DownloadSize);
       
       
       // Just transfer the whole block.
       // Just transfer the whole block.
       do
       do

+ 15 - 5
methods/server.cc

@@ -164,15 +164,22 @@ bool ServerState::HeaderLine(string Line)
 	 Encoding = Stream;
 	 Encoding = Stream;
       HaveContent = true;
       HaveContent = true;
 
 
-      unsigned long long * SizePtr = &Size;
+      unsigned long long * DownloadSizePtr = &DownloadSize;
       if (Result == 416)
       if (Result == 416)
-	 SizePtr = &JunkSize;
+	 DownloadSizePtr = &JunkSize;
 
 
-      *SizePtr = strtoull(Val.c_str(), NULL, 10);
-      if (*SizePtr >= std::numeric_limits<unsigned long long>::max())
+      *DownloadSizePtr = strtoull(Val.c_str(), NULL, 10);
+      if (*DownloadSizePtr >= std::numeric_limits<unsigned long long>::max())
 	 return _error->Errno("HeaderLine", _("The HTTP server sent an invalid Content-Length header"));
 	 return _error->Errno("HeaderLine", _("The HTTP server sent an invalid Content-Length header"));
-      else if (*SizePtr == 0)
+      else if (*DownloadSizePtr == 0)
 	 HaveContent = false;
 	 HaveContent = false;
+
+      // On partial content (206) the Content-Length less than the real
+      // size, so do not set it here but leave that to the Content-Range
+      // header instead
+      if(Result != 206 && Size == 0)
+         Size = DownloadSize;
+
       return true;
       return true;
    }
    }
 
 
@@ -193,6 +200,9 @@ bool ServerState::HeaderLine(string Line)
 	 return _error->Error(_("The HTTP server sent an invalid Content-Range header"));
 	 return _error->Error(_("The HTTP server sent an invalid Content-Range header"));
       if ((unsigned long long)StartPos > Size)
       if ((unsigned long long)StartPos > Size)
 	 return _error->Error(_("This HTTP server has broken range support"));
 	 return _error->Error(_("This HTTP server has broken range support"));
+
+      // figure out what we will download
+      DownloadSize = Size - StartPos;
       return true;
       return true;
    }
    }
 
 

+ 2 - 1
methods/server.h

@@ -34,7 +34,8 @@ struct ServerState
    char Code[360];
    char Code[360];
 
 
    // These are some statistics from the last parsed header lines
    // These are some statistics from the last parsed header lines
-   unsigned long long Size; // size of the usable content (aka: the file)
+   unsigned long long Size; // total size of the usable content (aka: the file)
+   unsigned long long DownloadSize; // size we actually download (can be smaller than Size if we have partial content)
    unsigned long long JunkSize; // size of junk content (aka: server error pages)
    unsigned long long JunkSize; // size of junk content (aka: server error pages)
    unsigned long long StartPos;
    unsigned long long StartPos;
    time_t Date;
    time_t Date;

+ 3 - 3
test/interactive-helper/aptwebserver.cc

@@ -654,13 +654,13 @@ static void * handleClient(void * voidclient)				/*{{{*/
 		     if (filesize > filestart)
 		     if (filesize > filestart)
 		     {
 		     {
 			data.Skip(filestart);
 			data.Skip(filestart);
-			std::ostringstream contentlength;
-			contentlength << "Content-Length: " << (filesize - filestart);
-			headers.push_back(contentlength.str());
 			std::ostringstream contentrange;
 			std::ostringstream contentrange;
 			contentrange << "Content-Range: bytes " << filestart << "-"
 			contentrange << "Content-Range: bytes " << filestart << "-"
 			   << filesize - 1 << "/" << filesize;
 			   << filesize - 1 << "/" << filesize;
 			headers.push_back(contentrange.str());
 			headers.push_back(contentrange.str());
+			std::ostringstream contentlength;
+			contentlength << "Content-Length: " << (filesize - filestart);
+			headers.push_back(contentlength.str());
 			sendHead(client, 206, headers);
 			sendHead(client, 206, headers);
 			if (sendContent == true)
 			if (sendContent == true)
 			   sendFile(client, headers, data);
 			   sendFile(client, headers, data);