]> git.saurik.com Git - apt.git/commitdiff
Fix endless loop in apt-get update that can cause disk fillup
authorMichael Vogt <mvo@ubuntu.com>
Fri, 22 May 2015 13:28:53 +0000 (15:28 +0200)
committerMichael Vogt <mvo@ubuntu.com>
Fri, 22 May 2015 13:28:53 +0000 (15:28 +0200)
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

methods/http.cc
methods/server.cc
methods/server.h
test/interactive-helper/aptwebserver.cc

index 1b996db9816bab01f688a645a0f855b90a0c138f..ad90c98912f32de585a263c00e8b7a18b854bf7f 100644 (file)
@@ -443,7 +443,7 @@ bool HttpServerState::RunData(FileFd * const File)
       else if (JunkSize != 0)
         In.Limit(JunkSize);
       else
-        In.Limit(Size - StartPos);
+        In.Limit(DownloadSize);
       
       // Just transfer the whole block.
       do
index e321e0230362a363702214d383edde2bbbf59301..ba0a8864b46c18c774f7d2fc7e7864d7944e593d 100644 (file)
@@ -164,15 +164,22 @@ bool ServerState::HeaderLine(string Line)
         Encoding = Stream;
       HaveContent = true;
 
-      unsigned long long * SizePtr = &Size;
+      unsigned long long * DownloadSizePtr = &DownloadSize;
       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"));
-      else if (*SizePtr == 0)
+      else if (*DownloadSizePtr == 0)
         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;
    }
 
@@ -193,6 +200,9 @@ bool ServerState::HeaderLine(string Line)
         return _error->Error(_("The HTTP server sent an invalid Content-Range header"));
       if ((unsigned long long)StartPos > Size)
         return _error->Error(_("This HTTP server has broken range support"));
+
+      // figure out what we will download
+      DownloadSize = Size - StartPos;
       return true;
    }
 
index 1b81e3549b5d05370700f1985aa532d2e3fff2aa..ed3cb456aae4898f780733c1b4552302cfe56651 100644 (file)
@@ -34,7 +34,8 @@ struct ServerState
    char Code[360];
 
    // 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 StartPos;
    time_t Date;
index cd52da692a8339cffdbab68ff61b039ea72275a8..9c67b67e485716f4d1fd4eadec048971a6a37213 100644 (file)
@@ -654,13 +654,13 @@ static void * handleClient(void * voidclient)                             /*{{{*/
                     if (filesize > filestart)
                     {
                        data.Skip(filestart);
-                       std::ostringstream contentlength;
-                       contentlength << "Content-Length: " << (filesize - filestart);
-                       headers.push_back(contentlength.str());
                        std::ostringstream contentrange;
                        contentrange << "Content-Range: bytes " << filestart << "-"
                           << filesize - 1 << "/" << filesize;
                        headers.push_back(contentrange.str());
+                       std::ostringstream contentlength;
+                       contentlength << "Content-Length: " << (filesize - filestart);
+                       headers.push_back(contentlength.str());
                        sendHead(client, 206, headers);
                        if (sendContent == true)
                           sendFile(client, headers, data);