Skip to content

Commit

Permalink
libct/nsenter: add json msg escaping
Browse files Browse the repository at this point in the history
Some strings logged by write_log() contain \n, which leads to errors
like this one:

> # time="2020-06-07T15:41:37Z" level=error msg="failed to decode \"{\\\"level\\\":\\\"debug\\\", \\\"msg\\\": \\\"nsexec-0[2265]: update /proc/2266/uid_map to '0 1000 1\\n\" to json: invalid character '\\n' in string literal"

The fix is to escape such characters.

Signed-off-by: Kir Kolyshkin <[email protected]>
  • Loading branch information
kolyshkin committed Jun 25, 2020
1 parent f27cb28 commit 6b06797
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 0 deletions.
91 changes: 91 additions & 0 deletions libcontainer/nsenter/escape.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include <stdlib.h>

static char hex(char i) {
if (i >= 0 && i < 10) {
return '0' + i;
}
if (i >= 10 && i < 16) {
return 'a' + i - 10;
}
return '?';
}

/*
* Escape the string to be usable as JSON string.
*/
char *escape_json_string(char *str) {
int i, j = 0;
char *out;

// Avoid malloc by checking first if escaping is required.
// While at it, count how much additional space we need.
// XXX: the counting code need to be in sync with the rest!
for (i = 0; str[i] != '\0'; i++) {
switch (str[i]) {
case '\\':
case '"':
case '\b':
case '\n':
case '\r':
case '\t':
case '\f':
j += 2;
break;
default:
if (str[i] < ' ') {
// \u00xx
j += 6;
}
}
}
if (j == 0) {
// nothing to escape
return str;
}

out = malloc(i + j);
if (!out) {
exit(1);
}
for (i = j = 0; str[i] != '\0'; i++, j++) {
switch (str[i]) {
case '"':
case '\\':
out[j++] = '\\';
out[j] = str[i];
continue;
}
if (str[i] >= ' ') {
out[j] = str[i];
continue;
}
out[j++] = '\\';
switch (str[i]) {
case '\b':
out[j] = 'b';
break;
case '\n':
out[j] = 'n';
break;
case '\r':
out[j] = 'r';
break;
case '\t':
out[j] = 't';
break;
case '\f':
out[j] = 'f';
break;
default:
out[j++] = 'u';
out[j++] = '0';
out[j++] = '0';
out[j++] = hex(str[i] >> 4);
out[j] = hex(str[i] & 0x0f);
}
}
out[j] = '\0';

free(str);
return out;
}
24 changes: 24 additions & 0 deletions libcontainer/nsenter/escape.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package nsenter

// This file is part of escape_json_string unit test. It would be a part
// of escape_test.go if Go would allow cgo to be used in _test.go files.

// #include <stdlib.h>
// #include "escape.h"
import "C"

import (
"testing"
"unsafe"
)

func testEscapeJsonString(t *testing.T, input, want string) {
in := C.CString(input)
out := C.escape_json_string(in)
got := C.GoString(out)
C.free(unsafe.Pointer(out))
t.Logf("input: %q, output: %q", input, got)
if got != want {
t.Errorf("Failed on input: %q, want %q, got %q", input, want, got)
}
}
1 change: 1 addition & 0 deletions libcontainer/nsenter/escape.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extern char *escape_json_string(char *str);
22 changes: 22 additions & 0 deletions libcontainer/nsenter/escape_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package nsenter

import "testing"

func TestEscapeJsonString(t *testing.T) {
testCases := []struct {
input, output string
}{
{"", ""},
{"abcdef", "abcdef"},
{`\\\\\\`, `\\\\\\\\\\\\`},
{`with"quote`, `with\"quote`},
{"\n\r\b\t\f\\", `\n\r\b\t\f\\`},
{"\007", "\\u0007"},
{"\017 \020 \037", "\\u000f \\u0010 \\u001f"},
{"\033", "\\u001b"},
}

for _, tc := range testCases {
testEscapeJsonString(t, tc.input, tc.output)
}
}
3 changes: 3 additions & 0 deletions libcontainer/nsenter/nsexec.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

/* Get all of the CLONE_NEW* flags. */
#include "namespace.h"
#include "escape.h"

/* Synchronisation values. */
enum sync_t {
Expand Down Expand Up @@ -153,6 +154,8 @@ static void write_log(const char *level, const char *format, ...)
if (ret < 0)
goto out;

message = escape_json_string(message);

if (current_stage == STAGE_SETUP)
stage = strdup("nsexec");
else
Expand Down

0 comments on commit 6b06797

Please sign in to comment.