]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Add Locker friend class to SBuf for protection against memory issues
authorAmos Jeffries <squid3@treenet.co.nz>
Fri, 30 Oct 2015 18:26:41 +0000 (11:26 -0700)
committerAmos Jeffries <squid3@treenet.co.nz>
Fri, 30 Oct 2015 18:26:41 +0000 (11:26 -0700)
When appending or otherwise modifying an SBuf based on a SBuf& or char*
the parameter used may be pointing at the MemBlob memory buffer
indirectly without holding a separate ref-count lock to it.

If 'this' SBuf then requires reallocation for any reason the char* or
buffer pointer taken from the SBuf&, which is being manipulated may in
fact be left pointing at invalid memory.

Utilize a private Locker class to create relatively cheap ref-count locks
on the store_ MemBlob when this problem MAY occur. This Locker needs to
be used on all non-const SBuf methods accepting char* or SBuf& argument.

src/SBuf.cc
src/SBuf.h

index e87df17fda457d80650776e78086576b2536be04..afbd709dbbb44e86042b2a467d3706aed99f4965 100644 (file)
@@ -149,6 +149,7 @@ SBuf::assign(const SBuf &S)
 SBuf&
 SBuf::assign(const char *S, size_type n)
 {
+    const Locker blobKeeper(this, S);
     debugs(24, 6, id << " from c-string, n=" << n << ")");
     clear();
     return append(S, n); //bounds checked in append()
@@ -202,12 +203,14 @@ SBuf::clear()
 SBuf&
 SBuf::append(const SBuf &S)
 {
+    const Locker blobKeeper(this, S.buf());
     return lowAppend(S.buf(), S.length());
 }
 
 SBuf &
 SBuf::append(const char * S, size_type Ssize)
 {
+    const Locker blobKeeper(this, S);
     if (S == NULL)
         return *this;
     if (Ssize == SBuf::npos)
@@ -226,6 +229,10 @@ SBuf::append(const char c)
 SBuf&
 SBuf::Printf(const char *fmt, ...)
 {
+    // with printf() the fmt or an arg might be a dangerous char*
+    // NP: cant rely on vappendf() Locker because of clear()
+    const Locker blobKeeper(this, buf());
+
     va_list args;
     va_start(args, fmt);
     clear();
@@ -247,6 +254,9 @@ SBuf::appendf(const char *fmt, ...)
 SBuf&
 SBuf::vappendf(const char *fmt, va_list vargs)
 {
+    // with (v)appendf() the fmt or an arg might be a dangerous char*
+    const Locker blobKeeper(this, buf());
+
     Must(fmt != NULL);
     int sz = 0;
     //reserve twice the format-string size, it's a likely heuristic
@@ -785,6 +795,10 @@ SBuf::findFirstNotOf(const CharacterSet &set, size_type startPos) const
 int
 SBuf::scanf(const char *format, ...)
 {
+    // with the format or an arg might be a dangerous char*
+    // that gets invalidated by c_str()
+    const Locker blobKeeper(this, buf());
+
     va_list arg;
     int rv;
     ++stats.scanf;
index ef77733dc1fae7e1baacbc2ed554077d1423458b..e9be770c994bc3674291dff599551bee8674f196 100644 (file)
@@ -545,6 +545,27 @@ public:
     // TODO: possibly implement a replace() call
 private:
 
+    /**
+     * Keeps SBuf's MemBlob alive in a blob-destroying context where
+     * a seemingly unrelated memory pointer may belong to the same blob.
+     * For [an extreme] example, consider: a.append(a).
+     * Compared to an SBuf temporary, this class is optimized to
+     * preserve blobs only if needed and to reduce debugging noise.
+     */
+    class Locker
+    {
+    public:
+        Locker(SBuf *parent, const char *otherBuffer) {
+            // lock if otherBuffer intersects the parents buffer area
+            const MemBlob *blob = parent->store_.getRaw();
+            if (blob->mem <= otherBuffer && otherBuffer < (blob->mem + blob->capacity))
+                locket = blob;
+        }
+    private:
+        MemBlob::Pointer locket;
+    };
+    friend class Locker;
+
     MemBlob::Pointer store_; ///< memory block, possibly shared with other SBufs
     size_type off_; ///< our content start offset from the beginning of shared store_
     size_type len_; ///< number of our content bytes in shared store_