Skip to content

Commit

Permalink
Escape NUL byte as '\0'. Fixes #1121
Browse files Browse the repository at this point in the history
  • Loading branch information
bgrainger committed Jan 25, 2022
1 parent d2ad209 commit 75cf89d
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 17 deletions.
49 changes: 32 additions & 17 deletions src/MySqlConnector/MySqlParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,16 +226,16 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
writer.Write((byte) '\'');
switch (charValue)
{
case '\'':
writer.Write((byte) '\'');
writer.Write((byte) charValue);
case '\0' when !noBackslashEscapes:
writer.Write((ushort) 0x305C); // \0
break;

case '\\':
if (!noBackslashEscapes)
writer.Write((byte) '\\');
case '\'':
writer.Write((ushort) 0x2727); // ''
break;

writer.Write((byte) charValue);
case '\\' when !noBackslashEscapes:
writer.Write((ushort) 0x5C5C); // \\
break;

default:
Expand Down Expand Up @@ -288,7 +288,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
var length = inputSpan.Length + BinaryBytes.Length + 1;
foreach (var by in inputSpan)
{
if (by is 0x27 || by is 0x5C && !noBackslashEscapes)
if (by is 0x27 || by is 0x00 or 0x5C && !noBackslashEscapes)
length++;
}

Expand All @@ -297,9 +297,18 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
var index = BinaryBytes.Length;
foreach (var by in inputSpan)
{
if (by is 0x27 || by is 0x5C && !noBackslashEscapes)
if (by == 0x00 && !noBackslashEscapes)
{
// \0
outputSpan[index++] = 0x5C;
outputSpan[index++] = 0x30;
}
else
{
if (by is 0x27 || by is 0x5C && !noBackslashEscapes)
outputSpan[index++] = by;
outputSpan[index++] = by;
outputSpan[index++] = by;
}
}
outputSpan[index++] = 0x27;
Debug.Assert(index == length, "index == length");
Expand Down Expand Up @@ -394,9 +403,9 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
writer.Write(BinaryBytes);
foreach (var by in bytes)
{
if (by is 0x27 or 0x5C)
if (by is 0x00 or 0x27 or 0x5C)
writer.Write((byte) 0x5C);
writer.Write(by);
writer.Write(by == 0 ? (byte) 0x30 : by);
}
writer.Write((byte) '\'');
}
Expand Down Expand Up @@ -471,7 +480,7 @@ static void WriteString(ByteBufferWriter writer, bool noBackslashEscapes, string
if (noBackslashEscapes)
writer.Write(value.Replace("'", "''"));
else
writer.Write(value.Replace("\\", "\\\\").Replace("'", "''"));
writer.Write(value.Replace("\\", "\\\\").Replace("'", "''").Replace("\0", "\\0"));

writer.Write((byte) '\'');
}
Expand All @@ -485,7 +494,7 @@ static void WriteString(ByteBufferWriter writer, bool noBackslashEscapes, bool w
while (charsWritten < value.Length)
{
var remainingValue = value.Slice(charsWritten);
var nextDelimiterIndex = remainingValue.IndexOfAny('\'', '\\');
var nextDelimiterIndex = remainingValue.IndexOfAny('\0', '\'', '\\');
if (nextDelimiterIndex == -1)
{
// write the rest of the string
Expand All @@ -495,11 +504,17 @@ static void WriteString(ByteBufferWriter writer, bool noBackslashEscapes, bool w
else
{
// write up to (and including) the delimiter, then double it
writer.Write(remainingValue.Slice(0, nextDelimiterIndex + 1), flush: true);
writer.Write(remainingValue.Slice(0, nextDelimiterIndex), flush: true);
if (remainingValue[nextDelimiterIndex] == '\\' && !noBackslashEscapes)
writer.Write((byte) '\\');
writer.Write((ushort) 0x5C5C); // \\
else if (remainingValue[nextDelimiterIndex] == '\\' && noBackslashEscapes)
writer.Write((byte) 0x5C); // \
else if (remainingValue[nextDelimiterIndex] == '\'')
writer.Write((byte) '\'');
writer.Write((ushort) 0x2727); // ''
else if (remainingValue[nextDelimiterIndex] == '\0' && !noBackslashEscapes)
writer.Write((ushort) 0x305C); // \0
else if (remainingValue[nextDelimiterIndex] == '\0' && noBackslashEscapes)
writer.Write((byte) 0x00); // (nul)
charsWritten += nextDelimiterIndex + 1;
}
}
Expand Down
26 changes: 26 additions & 0 deletions tests/SideBySide/InsertTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,32 @@ value set('one', 'two', 'four', 'eight') null
Assert.Equal(new[] { "one", "one,two", "one,four", "one,two,four" }, m_database.Connection.Query<string>(@"select value from insert_mysql_set where find_in_set('one', value) order by rowid"));
}

[Theory]
[MemberData(nameof(GetChars))]
public void InsertChar(char ch, bool prepare)
{
using var connection = new MySqlConnection(AppConfig.ConnectionString);
connection.Open();
connection.Execute(@"drop table if exists insert_char;
create table insert_char(
rowid integer not null primary key auto_increment,
value tinytext null
);");

using (var cmd = new MySqlCommand("insert into insert_char(value) values(@data);", connection))
{
cmd.Parameters.AddWithValue("@data", ch);
if (prepare)
cmd.Prepare();
cmd.ExecuteNonQuery();
}
Assert.Equal(ch, connection.Query<char>(@"select value from insert_char;").Single());
}

public static IEnumerable<object[]> GetChars() =>
new[] { '\0', 'a', '\'', '\"', '\\', 'A', '\b', '\n', '\r', '\t', '\x1A' }
.SelectMany(x => new[] { false, true }.Select(y => new object[] { x, y }));


#if !BASELINE
[Theory]
Expand Down

0 comments on commit 75cf89d

Please sign in to comment.