Skip to content

Commit 3b0706b

Browse files
author
Arun S
committed
procfs: add KernelTainted() to parse /proc/sys/kernel/tainted
Signed-off-by: Arun S <arun.srinivasan@flipkart.com>
1 parent d924a52 commit 3b0706b

3 files changed

Lines changed: 209 additions & 0 deletions

File tree

kernel_tainted.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
//go:build !windows
15+
16+
package procfs
17+
18+
import (
19+
"bytes"
20+
"fmt"
21+
"io"
22+
"strconv"
23+
"strings"
24+
25+
"github.com/prometheus/procfs/internal/util"
26+
)
27+
28+
// KernelTaintBit represents a single kernel taint flag.
29+
type KernelTaintBit struct {
30+
// Index is the zero-based bit position in the tainted bitmask.
31+
Index int
32+
// Flag is the letter code identifying this taint (e.g. "L" for soft lockup).
33+
// See https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html
34+
Flag string
35+
// Description is a human-readable explanation of what this taint means.
36+
Description string
37+
// Set indicates whether this taint flag is currently active.
38+
Set bool
39+
}
40+
41+
// KernelTainted holds the kernel taint state read from /proc/sys/kernel/tainted.
42+
type KernelTainted struct {
43+
// Value is the raw integer from /proc/sys/kernel/tainted.
44+
Value uint64
45+
// Bits contains each known taint flag and whether it is currently set.
46+
// Flags are listed in bit-position order (bit 0 first).
47+
Bits []KernelTaintBit
48+
}
49+
50+
// kernelTaintBitDefs lists all known kernel taint flags in bit-position order.
51+
// Reference: https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html
52+
var kernelTaintBitDefs = []struct {
53+
flag string
54+
description string
55+
}{
56+
{"P", "Proprietary module was loaded."},
57+
{"F", "Module was force loaded."},
58+
{"S", "SMP kernel oops on an officially SMP incapable processor."},
59+
{"R", "Module was force unloaded."},
60+
{"M", "Processor reported a Machine Check Exception (MCE)."},
61+
{"B", "Bad page referenced or some unexpected page flags."},
62+
{"U", "Taint requested by userspace application."},
63+
{"D", "Kernel died recently, i.e. there was an OOPS or BUG."},
64+
{"A", "An ACPI table was overridden by user."},
65+
{"W", "Kernel issued warning."},
66+
{"C", "Staging driver was loaded."},
67+
{"I", "Workaround for bug in platform firmware applied."},
68+
{"O", "Externally-built (out-of-tree) module was loaded."},
69+
{"E", "Unsigned module was loaded."},
70+
{"L", "Soft lockup occurred."},
71+
{"K", "Kernel has been live patched."},
72+
{"X", "Auxiliary taint, defined and used by distros."},
73+
{"T", "The kernel was built with the struct randomization plugin."},
74+
{"N", "An in-kernel test such as a KUnit test has been run."},
75+
{"J", "Userspace used a mutating debug operation in fwctl."},
76+
}
77+
78+
// KernelTainted reads /proc/sys/kernel/tainted and returns the raw taint value
79+
// along with each known flag parsed into a KernelTainted struct.
80+
// See https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html
81+
func (fs FS) KernelTainted() (KernelTainted, error) {
82+
data, err := util.ReadFileNoStat(fs.proc.Path("sys", "kernel", "tainted"))
83+
if err != nil {
84+
return KernelTainted{}, err
85+
}
86+
return parseTainted(bytes.NewReader(data))
87+
}
88+
89+
// parseTainted parses the content of /proc/sys/kernel/tainted.
90+
func parseTainted(r io.Reader) (KernelTainted, error) {
91+
data, err := io.ReadAll(r)
92+
if err != nil {
93+
return KernelTainted{}, err
94+
}
95+
96+
value, err := strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
97+
if err != nil {
98+
return KernelTainted{}, fmt.Errorf("couldn't parse tainted value: %w", err)
99+
}
100+
101+
bits := make([]KernelTaintBit, len(kernelTaintBitDefs))
102+
for i, def := range kernelTaintBitDefs {
103+
bits[i] = KernelTaintBit{
104+
Index: i,
105+
Flag: def.flag,
106+
Description: def.description,
107+
Set: value&(1<<uint(i)) != 0,
108+
}
109+
}
110+
111+
return KernelTainted{
112+
Value: value,
113+
Bits: bits,
114+
}, nil
115+
}

kernel_tainted_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
//go:build !windows
15+
16+
package procfs
17+
18+
import (
19+
"strings"
20+
"testing"
21+
)
22+
23+
func TestKernelTainted(t *testing.T) {
24+
fs, err := NewFS(procfsFixtures)
25+
if err != nil {
26+
t.Fatalf("failed to access %s: %v", procfsFixtures, err)
27+
}
28+
29+
tainted, err := fs.KernelTainted()
30+
if err != nil {
31+
t.Fatalf("failed to collect %s/sys/kernel/tainted: %v", procfsFixtures, err)
32+
}
33+
34+
// Fixture value is 12288 = bit 12 (O) + bit 13 (E).
35+
if tainted.Value != 12288 {
36+
t.Errorf("tainted value: want %d, got %d", 12288, tainted.Value)
37+
}
38+
39+
const wantBits = 20
40+
if len(tainted.Bits) != wantBits {
41+
t.Fatalf("tainted bits length: want %d, got %d", wantBits, len(tainted.Bits))
42+
}
43+
44+
for _, tc := range []struct {
45+
index int
46+
flag string
47+
set bool
48+
}{
49+
{0, "P", false},
50+
{12, "O", true}, // Externally-built (out-of-tree) module
51+
{13, "E", true}, // Unsigned module
52+
{14, "L", false}, // Soft lockup — must be clear
53+
{17, "T", false},
54+
{18, "N", false},
55+
{19, "J", false},
56+
} {
57+
b := tainted.Bits[tc.index]
58+
if b.Index != tc.index {
59+
t.Errorf("bit[%d].Index: want %d, got %d", tc.index, tc.index, b.Index)
60+
}
61+
if b.Flag != tc.flag {
62+
t.Errorf("bit[%d].Flag: want %q, got %q", tc.index, tc.flag, b.Flag)
63+
}
64+
if b.Set != tc.set {
65+
t.Errorf("bit[%d] (%s): want Set=%v, got Set=%v", tc.index, tc.flag, tc.set, b.Set)
66+
}
67+
}
68+
}
69+
70+
func TestParseTainted(t *testing.T) {
71+
// Test parsing directly without filesystem access.
72+
tainted, err := parseTainted(strings.NewReader("16384\n"))
73+
if err != nil {
74+
t.Fatalf("parseTainted failed: %v", err)
75+
}
76+
77+
// 16384 = 2^14 → only bit 14 (L, soft lockup) should be set.
78+
if tainted.Value != 16384 {
79+
t.Errorf("value: want 16384, got %d", tainted.Value)
80+
}
81+
if !tainted.Bits[14].Set {
82+
t.Errorf("bit 14 (L): want Set=true, got false")
83+
}
84+
for i, b := range tainted.Bits {
85+
if i != 14 && b.Set {
86+
t.Errorf("bit %d (%s): want Set=false, got true", i, b.Flag)
87+
}
88+
}
89+
}

testdata/fixtures.ttar

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3465,6 +3465,11 @@ Lines: 1
34653465
kill_process kill_thread trap errno trace log allow
34663466
Mode: 644
34673467
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3468+
Path: fixtures/proc/sys/kernel/tainted
3469+
Lines: 1
3470+
12288
3471+
Mode: 444
3472+
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
34683473
Directory: fixtures/proc/sys/vm
34693474
Mode: 755
34703475
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

0 commit comments

Comments
 (0)