NetworkCookieJar: use-after-free, memcpy overwrite

* Add some tracing, std::nothrow and null checks
* The HashString class doesn't like SetTo being called with a substring
of the current key, so use a copy of it instead.

Fixes #6667.
This commit is contained in:
Adrien Destugues
2014-01-16 16:54:51 +01:00
parent 547c1486ff
commit e395fc4f3b
+44 -21
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2013 Haiku Inc. All rights reserved. * Copyright 2010-2014 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -8,30 +8,38 @@
*/ */
#include <Debug.h> #include <new>
#include <stdio.h>
#include <HashMap.h> #include <HashMap.h>
#include <HashString.h> #include <HashString.h>
#include <Message.h> #include <Message.h>
#include <NetworkCookieJar.h> #include <NetworkCookieJar.h>
#include <new>
#include "NetworkCookieJarPrivate.h" #include "NetworkCookieJarPrivate.h"
// #define TRACE_COOKIE
#ifdef TRACE_COOKIE
# define TRACE(x...) printf(x)
#else
# define TRACE(x...) ;
#endif
const char* kArchivedCookieMessageName = "be:cookie"; const char* kArchivedCookieMessageName = "be:cookie";
BNetworkCookieJar::BNetworkCookieJar() BNetworkCookieJar::BNetworkCookieJar()
: :
fCookieHashMap(new PrivateHashMap()) fCookieHashMap(new(std::nothrow) PrivateHashMap())
{ {
} }
BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieJar& other) BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieJar& other)
: :
fCookieHashMap(new PrivateHashMap()) fCookieHashMap(new(std::nothrow) PrivateHashMap())
{ {
*this = other; *this = other;
} }
@@ -39,7 +47,7 @@ BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieJar& other)
BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieList& otherList) BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieList& otherList)
: :
fCookieHashMap(new PrivateHashMap()) fCookieHashMap(new(std::nothrow) PrivateHashMap())
{ {
AddCookies(otherList); AddCookies(otherList);
} }
@@ -47,7 +55,7 @@ BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieList& otherList)
BNetworkCookieJar::BNetworkCookieJar(BMessage* archive) BNetworkCookieJar::BNetworkCookieJar(BMessage* archive)
: :
fCookieHashMap(new PrivateHashMap()) fCookieHashMap(new(std::nothrow) PrivateHashMap())
{ {
BMessage extractedCookie; BMessage extractedCookie;
@@ -117,11 +125,16 @@ BNetworkCookieJar::AddCookie(const BString& cookie, const BUrl& referrer)
status_t status_t
BNetworkCookieJar::AddCookie(BNetworkCookie* cookie) BNetworkCookieJar::AddCookie(BNetworkCookie* cookie)
{ {
if (fCookieHashMap == NULL)
return B_NO_MEMORY;
if (cookie == NULL || !cookie->IsValid()) if (cookie == NULL || !cookie->IsValid())
return B_BAD_VALUE; return B_BAD_VALUE;
HashString key(cookie->Domain()); HashString key(cookie->Domain());
// Get the cookies for the requested domain, or create a new list if there
// isn't one yet.
BNetworkCookieList* list = fCookieHashMap->fHashMap.Get(key); BNetworkCookieList* list = fCookieHashMap->fHashMap.Get(key);
if (list == NULL) { if (list == NULL) {
list = new(std::nothrow) BNetworkCookieList(); list = new(std::nothrow) BNetworkCookieList();
@@ -129,6 +142,8 @@ BNetworkCookieJar::AddCookie(BNetworkCookie* cookie)
return B_NO_MEMORY; return B_NO_MEMORY;
} }
// Remove any cookie with the same key as the one we're trying to add (it
// replaces/updates them)
for (int32 i = 0; i < list->CountItems(); i++) { for (int32 i = 0; i < list->CountItems(); i++) {
BNetworkCookie* c = list->ItemAt(i); BNetworkCookie* c = list->ItemAt(i);
@@ -138,9 +153,13 @@ BNetworkCookieJar::AddCookie(BNetworkCookie* cookie)
} }
} }
if (cookie->ShouldDeleteNow()) // If the cookie has an expiration date in the past, stop here: we
// effectively deleted a cookie.
if (cookie->ShouldDeleteNow()) {
TRACE("Remove cookie: %s\n", cookie->RawCookie(true).String());
delete cookie; delete cookie;
else { } else {
TRACE("Add cookie: %s\n", cookie->RawCookie(true).String());
list->AddItem(cookie); list->AddItem(cookie);
} }
@@ -370,7 +389,7 @@ BNetworkCookieJar::operator=(const BNetworkCookieJar& other)
fFlattened = other.fFlattened; fFlattened = other.fFlattened;
delete fCookieHashMap; delete fCookieHashMap;
fCookieHashMap = new PrivateHashMap(); fCookieHashMap = new(std::nothrow) PrivateHashMap();
for (Iterator it = other.GetIterator(); it.HasNext();) { for (Iterator it = other.GetIterator(); it.HasNext();) {
BNetworkCookie* cookie = it.Next(); BNetworkCookie* cookie = it.Next();
@@ -555,7 +574,7 @@ BNetworkCookieJar::Iterator::_FindNext()
return; return;
} }
if (!fIterator->fCookieMapIterator.HasNext()) { if (fIterator == NULL || !fIterator->fCookieMapIterator.HasNext()) {
fElement = NULL; fElement = NULL;
return; return;
} }
@@ -592,11 +611,11 @@ BNetworkCookieJar::UrlIterator::UrlIterator(const BNetworkCookieJar* cookieJar,
BString domain = url.Host(); BString domain = url.Host();
if (!domain.Length()) { if (!domain.Length()) {
if (url.Protocol() == "file") if (url.Protocol() == "file")
domain = "localhost"; domain = "localhost";
else else
return; return;
} }
fIterator = new(std::nothrow) PrivateIterator( fIterator = new(std::nothrow) PrivateIterator(
fCookieJar->fCookieHashMap->fHashMap.GetIterator()); fCookieJar->fCookieHashMap->fHashMap.GetIterator());
@@ -683,12 +702,16 @@ BNetworkCookieJar::UrlIterator::operator=(
bool bool
BNetworkCookieJar::UrlIterator::_SuperDomain() BNetworkCookieJar::UrlIterator::_SuperDomain()
{ {
const char* domain = fIterator->fKey.GetString(); BString domain(fIterator->fKey.GetString());
const char* nextDot = strchr(domain, '.'); // Makes a copy of the characters from the key. This is important,
// because HashString doesn't like SetTo to be called with a substring
if (nextDot == NULL) // of its original string (use-after-free + memcpy overwrite).
int32 firstDot = domain.FindFirst('.');
if (firstDot < 0)
return false; return false;
const char* nextDot = domain.String() + firstDot;
fIterator->fKey.SetTo(nextDot + 1); fIterator->fKey.SetTo(nextDot + 1);
return true; return true;
} }