From 52abbe8c2f7f1141d539696ab343e103d97b3bd0 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 7 Sep 2021 15:16:09 -0700 Subject: [PATCH] readCon speedup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When looking at runc strace output, I noticed that selinux attr files are read byte by byte: read(8, "u", 1) = 1 read(8, "n", 1) = 1 .... Apparently, this happens because fmt.Fscanf("%s") works that way. Let's read the whole file via ioutil.ReadAll instead. The added benchmark shows a drastic improvement: name old time/op new time/op delta CurrentLabel-4 26.4µs ± 1% 4.3µs ± 1% -83.73% (p=0.004 n=6+5) name old alloc/op new alloc/op delta CurrentLabel-4 352B ± 0% 776B ± 0% +120.45% (p=0.002 n=6+6) name old allocs/op new allocs/op delta CurrentLabel-4 9.00 ± 0% 7.00 ± 0% -22.22% (p=0.002 n=6+6) Note that increased allocation size is caused by the fact that ReadAll preallocates a 512 bytes buffer, which is very reasonable. Signed-off-by: Kir Kolyshkin --- go-selinux/selinux_linux.go | 15 +++++++-------- go-selinux/selinux_linux_test.go | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/go-selinux/selinux_linux.go b/go-selinux/selinux_linux.go index a804473..2402f4b 100644 --- a/go-selinux/selinux_linux.go +++ b/go-selinux/selinux_linux.go @@ -274,12 +274,15 @@ func readCon(fpath string) (string, error) { if err := isProcHandle(in); err != nil { return "", err } + return readConFd(in) +} - var retval string - if _, err := fmt.Fscanf(in, "%s", &retval); err != nil { +func readConFd(in *os.File) (string, error) { + data, err := ioutil.ReadAll(in) + if err != nil { return "", err } - return strings.Trim(retval, "\x00"), nil + return string(bytes.TrimSuffix(data, []byte{0})), nil } // classIndex returns the int index for an object class in the loaded policy, @@ -664,11 +667,7 @@ func readWriteCon(fpath string, val string) (string, error) { return "", err } - var retval string - if _, err := fmt.Fscanf(f, "%s", &retval); err != nil { - return "", err - } - return strings.Trim(retval, "\x00"), nil + return readConFd(f) } // setExecLabel sets the SELinux label that the kernel will use for any programs diff --git a/go-selinux/selinux_linux_test.go b/go-selinux/selinux_linux_test.go index 659b8fa..54291b4 100644 --- a/go-selinux/selinux_linux_test.go +++ b/go-selinux/selinux_linux_test.go @@ -509,3 +509,17 @@ func BenchmarkChcon(b *testing.B) { } } } + +func BenchmarkCurrentLabel(b *testing.B) { + var ( + l string + err error + ) + for n := 0; n < b.N; n++ { + l, err = CurrentLabel() + if err != nil { + b.Fatal(err) + } + } + b.Log(l) +}