Commit 4d94d132 authored by Ryan Sleevi's avatar Ryan Sleevi Committed by Commit Bot

Explicitly handle zero-byte NTLM buffer writes

r558579 changed the backing store of NTLM writes from being a
std::basic_string<uint8_t> to a std::vector<uint8_t>, to better align
with its purpose. This change had the effect of causing .data() to now
return nullptr when .size() == 0, rather than the previous behaviour,
in which .data() would return a 1-byte buffer that was a NUL ('\0')
terminator - the consequence of using std::basic_string<>.

This had the effect on writes which expected zero-byte writes (aka
no-ops) to succeed, in that GetBufferPtr() now returned nullptr, rather
than a valid pointer. Since zero-byte memcpys immediately follow, this
needs to be explicitly handled, as memcpy() requires both pointers be
valid, even for zero-byte writes.

BUG=843066

Change-Id: I9ff8f825469142da31abc866c81f0418ae25f93f
Reviewed-on: https://chromium-review.googlesource.com/1059449
Commit-Queue: Ryan Sleevi <rsleevi@chromium.org>
Reviewed-by: default avatarAsanka Herath <asanka@chromium.org>
Cr-Commit-Position: refs/heads/master@{#558812}
parent 4dd6975d
...@@ -54,6 +54,9 @@ bool NtlmBufferReader::ReadBytes(base::span<uint8_t> buffer) { ...@@ -54,6 +54,9 @@ bool NtlmBufferReader::ReadBytes(base::span<uint8_t> buffer) {
if (!CanRead(buffer.size())) if (!CanRead(buffer.size()))
return false; return false;
if (buffer.empty())
return true;
memcpy(buffer.data(), GetBufferAtCursor(), buffer.size()); memcpy(buffer.data(), GetBufferAtCursor(), buffer.size());
AdvanceCursor(buffer.size()); AdvanceCursor(buffer.size());
...@@ -65,6 +68,9 @@ bool NtlmBufferReader::ReadBytesFrom(const SecurityBuffer& sec_buf, ...@@ -65,6 +68,9 @@ bool NtlmBufferReader::ReadBytesFrom(const SecurityBuffer& sec_buf,
if (!CanReadFrom(sec_buf) || buffer.size() < sec_buf.length) if (!CanReadFrom(sec_buf) || buffer.size() < sec_buf.length)
return false; return false;
if (buffer.empty())
return true;
memcpy(buffer.data(), GetBufferPtr() + sec_buf.offset, sec_buf.length); memcpy(buffer.data(), GetBufferPtr() + sec_buf.offset, sec_buf.length);
return true; return true;
......
...@@ -41,6 +41,24 @@ TEST(NtlmBufferReaderTest, EmptyBuffer) { ...@@ -41,6 +41,24 @@ TEST(NtlmBufferReaderTest, EmptyBuffer) {
ASSERT_TRUE(reader.CanRead(0)); ASSERT_TRUE(reader.CanRead(0));
ASSERT_FALSE(reader.CanRead(1)); ASSERT_FALSE(reader.CanRead(1));
ASSERT_TRUE(reader.IsEndOfBuffer()); ASSERT_TRUE(reader.IsEndOfBuffer());
// A read from an empty (zero-byte) source into an empty (zero-byte)
// destination buffer should succeed as a no-op.
std::vector<uint8_t> dest;
ASSERT_TRUE(reader.ReadBytes(dest));
// A read from a non-empty source into an empty (zero-byte) destination
// buffer should succeed as a no-op.
std::vector<uint8_t> b2{0x01};
NtlmBufferReader reader2(b2);
ASSERT_EQ(0u, reader2.GetCursor());
ASSERT_EQ(1u, reader2.GetLength());
ASSERT_TRUE(reader2.CanRead(0));
ASSERT_TRUE(reader2.ReadBytes(dest));
ASSERT_EQ(0u, reader2.GetCursor());
ASSERT_EQ(1u, reader2.GetLength());
} }
TEST(NtlmBufferReaderTest, NullBuffer) { TEST(NtlmBufferReaderTest, NullBuffer) {
...@@ -51,6 +69,11 @@ TEST(NtlmBufferReaderTest, NullBuffer) { ...@@ -51,6 +69,11 @@ TEST(NtlmBufferReaderTest, NullBuffer) {
ASSERT_TRUE(reader.CanRead(0)); ASSERT_TRUE(reader.CanRead(0));
ASSERT_FALSE(reader.CanRead(1)); ASSERT_FALSE(reader.CanRead(1));
ASSERT_TRUE(reader.IsEndOfBuffer()); ASSERT_TRUE(reader.IsEndOfBuffer());
// A read from a null source into an empty (zero-byte) destination buffer
// should succeed as a no-op.
std::vector<uint8_t> dest;
ASSERT_TRUE(reader.ReadBytes(dest));
} }
TEST(NtlmBufferReaderTest, Read16) { TEST(NtlmBufferReaderTest, Read16) {
......
...@@ -6,12 +6,12 @@ ...@@ -6,12 +6,12 @@
#include <string.h> #include <string.h>
#include <limits>
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "build/build_config.h" #include "build/build_config.h"
template class std::basic_string<uint8_t>;
namespace net { namespace net {
namespace ntlm { namespace ntlm {
...@@ -21,14 +21,14 @@ NtlmBufferWriter::NtlmBufferWriter(size_t buffer_len) ...@@ -21,14 +21,14 @@ NtlmBufferWriter::NtlmBufferWriter(size_t buffer_len)
NtlmBufferWriter::~NtlmBufferWriter() = default; NtlmBufferWriter::~NtlmBufferWriter() = default;
bool NtlmBufferWriter::CanWrite(size_t len) const { bool NtlmBufferWriter::CanWrite(size_t len) const {
if (len == 0)
return true;
if (!GetBufferPtr()) if (!GetBufferPtr())
return false; return false;
DCHECK_LE(GetCursor(), GetLength()); DCHECK_LE(GetCursor(), GetLength());
if (len == 0)
return true;
return (len <= GetLength()) && (GetCursor() <= GetLength() - len); return (len <= GetLength()) && (GetCursor() <= GetLength() - len);
} }
...@@ -49,6 +49,9 @@ bool NtlmBufferWriter::WriteFlags(NegotiateFlags flags) { ...@@ -49,6 +49,9 @@ bool NtlmBufferWriter::WriteFlags(NegotiateFlags flags) {
} }
bool NtlmBufferWriter::WriteBytes(base::span<const uint8_t> bytes) { bool NtlmBufferWriter::WriteBytes(base::span<const uint8_t> bytes) {
if (bytes.size() == 0)
return true;
if (!CanWrite(bytes.size())) if (!CanWrite(bytes.size()))
return false; return false;
...@@ -58,6 +61,9 @@ bool NtlmBufferWriter::WriteBytes(base::span<const uint8_t> bytes) { ...@@ -58,6 +61,9 @@ bool NtlmBufferWriter::WriteBytes(base::span<const uint8_t> bytes) {
} }
bool NtlmBufferWriter::WriteZeros(size_t count) { bool NtlmBufferWriter::WriteZeros(size_t count) {
if (count == 0)
return true;
if (!CanWrite(count)) if (!CanWrite(count))
return false; return false;
...@@ -113,7 +119,13 @@ bool NtlmBufferWriter::WriteUtf8AsUtf16String(const std::string& str) { ...@@ -113,7 +119,13 @@ bool NtlmBufferWriter::WriteUtf8AsUtf16String(const std::string& str) {
} }
bool NtlmBufferWriter::WriteUtf16String(const base::string16& str) { bool NtlmBufferWriter::WriteUtf16String(const base::string16& str) {
if (str.size() > std::numeric_limits<size_t>::max() / 2)
return false;
size_t num_bytes = str.size() * 2; size_t num_bytes = str.size() * 2;
if (num_bytes == 0)
return true;
if (!CanWrite(num_bytes)) if (!CanWrite(num_bytes))
return false; return false;
......
...@@ -37,6 +37,42 @@ TEST(NtlmBufferWriterTest, Initialization) { ...@@ -37,6 +37,42 @@ TEST(NtlmBufferWriterTest, Initialization) {
ASSERT_FALSE(writer.CanWrite(2)); ASSERT_FALSE(writer.CanWrite(2));
} }
TEST(NtlmBufferWriterTest, EmptyWrite) {
NtlmBufferWriter writer(0);
ASSERT_EQ(0u, writer.GetLength());
ASSERT_EQ(0u, writer.GetBuffer().size());
ASSERT_EQ(0u, writer.GetCursor());
ASSERT_EQ(nullptr, GetBufferPtr(writer));
// An empty (zero-byte) write into a zero-byte writer should succeed as a
// no-op.
std::vector<uint8_t> b;
ASSERT_TRUE(writer.CanWrite(0));
ASSERT_TRUE(writer.WriteBytes(b));
ASSERT_EQ(0u, writer.GetLength());
ASSERT_EQ(0u, writer.GetBuffer().size());
ASSERT_EQ(0u, writer.GetCursor());
ASSERT_EQ(nullptr, GetBufferPtr(writer));
// An empty (zero-byte) write into a non-zero-byte writer should succeed as
// a no-op.
NtlmBufferWriter writer2(1);
ASSERT_EQ(1u, writer2.GetLength());
ASSERT_EQ(1u, writer2.GetBuffer().size());
ASSERT_EQ(0u, writer2.GetCursor());
ASSERT_NE(nullptr, GetBufferPtr(writer2));
ASSERT_TRUE(writer2.CanWrite(0));
ASSERT_TRUE(writer2.WriteBytes(b));
ASSERT_EQ(1u, writer2.GetLength());
ASSERT_EQ(1u, writer2.GetBuffer().size());
ASSERT_EQ(0u, writer2.GetCursor());
ASSERT_NE(nullptr, GetBufferPtr(writer2));
}
TEST(NtlmBufferWriterTest, Write16) { TEST(NtlmBufferWriterTest, Write16) {
uint8_t expected[2] = {0x22, 0x11}; uint8_t expected[2] = {0x22, 0x11};
const uint16_t value = 0x1122; const uint16_t value = 0x1122;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment