Skip to content

Commit c63efca

Browse files
rhatdanningmingxiao
authored andcommitted
Merge pull request #254 from kolyshkin/cut
Use Cut more Signed-off-by: ningmingxiao <ning.mingxiao@zte.com.cn>
2 parents 9223b3e + 581ebad commit c63efca

4 files changed

Lines changed: 106 additions & 97 deletions

File tree

go-selinux/label/label_linux.go

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var validOptions = map[string]bool{
1717
"role": true,
1818
"level": true,
1919
}
20+
var maxSelinuxLabelSize = int(selinux.CategoryRange * (selinux.CategoryRange - 1) / 2)
2021

2122
var ErrIncompatibleLabel = errors.New("bad SELinux option: z and Z can not be used together")
2223

@@ -30,52 +31,54 @@ func InitLabels(options []string) (plabel string, mlabel string, retErr error) {
3031
if !selinux.GetEnabled() {
3132
return "", "", nil
3233
}
34+
if size := selinux.ContainerLabelsSize(); size >= maxSelinuxLabelSize {
35+
return "", "", fmt.Errorf("SELinux label exhaustion: %d labels used", size)
36+
}
3337
processLabel, mountLabel := selinux.ContainerLabels()
34-
if processLabel != "" {
35-
defer func() {
36-
if retErr != nil {
37-
selinux.ReleaseLabel(mountLabel)
38-
}
39-
}()
40-
pcon, err := selinux.NewContext(processLabel)
41-
if err != nil {
42-
return "", "", err
38+
if processLabel == "" {
39+
// processLabel is required; if empty, do nothing.
40+
return processLabel, mountLabel, nil
41+
}
42+
defer func() {
43+
if retErr != nil {
44+
selinux.ReleaseLabel(mountLabel)
4345
}
44-
mcsLevel := pcon["level"]
45-
mcon, err := selinux.NewContext(mountLabel)
46-
if err != nil {
47-
return "", "", err
46+
}()
47+
pcon, err := selinux.NewContext(processLabel)
48+
if err != nil {
49+
return "", "", err
50+
}
51+
mcsLevel := pcon["level"]
52+
mcon, err := selinux.NewContext(mountLabel)
53+
if err != nil {
54+
return "", "", err
55+
}
56+
for _, opt := range options {
57+
if opt == "disable" {
58+
selinux.ReleaseLabel(mountLabel)
59+
return "", selinux.PrivContainerMountLabel(), nil
60+
}
61+
k, v, ok := strings.Cut(opt, ":")
62+
if !ok || !validOptions[k] {
63+
return "", "", fmt.Errorf("bad label option %q, valid options 'disable' or \n'user, role, level, type, filetype' followed by ':' and a value", opt)
4864
}
49-
for _, opt := range options {
50-
if opt == "disable" {
51-
selinux.ReleaseLabel(mountLabel)
52-
return "", selinux.PrivContainerMountLabel(), nil
53-
}
54-
if !strings.Contains(opt, ":") {
55-
return "", "", fmt.Errorf("bad label option %q, valid options 'disable' or \n'user, role, level, type, filetype' followed by ':' and a value", opt)
56-
}
57-
con := strings.SplitN(opt, ":", 2)
58-
if !validOptions[con[0]] {
59-
return "", "", fmt.Errorf("bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0])
60-
}
61-
if con[0] == "filetype" {
62-
mcon["type"] = con[1]
63-
continue
64-
}
65-
pcon[con[0]] = con[1]
66-
if con[0] == "level" || con[0] == "user" {
67-
mcon[con[0]] = con[1]
68-
}
65+
if k == "filetype" {
66+
mcon["type"] = v
67+
continue
6968
}
70-
if pcon.Get() != processLabel {
71-
if pcon["level"] != mcsLevel {
72-
selinux.ReleaseLabel(processLabel)
73-
}
74-
processLabel = pcon.Get()
75-
selinux.ReserveLabel(processLabel)
69+
pcon[k] = v
70+
if k == "level" || k == "user" {
71+
mcon[k] = v
72+
}
73+
}
74+
if p := pcon.Get(); p != processLabel {
75+
if pcon["level"] != mcsLevel {
76+
selinux.ReleaseLabel(processLabel)
7677
}
77-
mountLabel = mcon.Get()
78+
selinux.ReserveLabel(p)
79+
processLabel = p
7880
}
81+
mountLabel = mcon.Get()
7982
return processLabel, mountLabel, nil
8083
}
8184

go-selinux/label/label_linux_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package label
33
import (
44
"errors"
55
"os"
6+
"strings"
67
"testing"
78

89
"github.com/opencontainers/selinux/go-selinux"
@@ -53,6 +54,22 @@ func TestInit(t *testing.T) {
5354
}
5455
}
5556

57+
func TestSELinuxLabelExhaustion(t *testing.T) {
58+
needSELinux(t)
59+
selinux.CategoryRange = 5
60+
var testNull []string
61+
for i := 0; i < 20; i++ {
62+
_, _, err := InitLabels(testNull)
63+
if i == 19 {
64+
if err == nil {
65+
t.Fatal("err should not be nil")
66+
} else if !strings.Contains(err.Error(), "SELinux label exhaustion") {
67+
t.Fatalf("unexpected error %s", err.Error())
68+
}
69+
}
70+
}
71+
}
72+
5673
func TestRelabel(t *testing.T) {
5774
needSELinux(t)
5875

go-selinux/selinux_linux.go

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ type selinuxState struct {
4444
selinuxfsOnce sync.Once
4545
enabledSet bool
4646
enabled bool
47-
sync.Mutex
47+
sync.RWMutex
4848
}
4949

5050
type level struct {
@@ -92,6 +92,12 @@ var policyRoot = sync.OnceValue(func() string {
9292
return filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
9393
})
9494

95+
func ContainerLabelsSize() int {
96+
state.RLock()
97+
defer state.RUnlock()
98+
return len(state.mcsList)
99+
}
100+
95101
func (s *selinuxState) setEnable(enabled bool) bool {
96102
s.Lock()
97103
defer s.Unlock()
@@ -243,12 +249,9 @@ func readConfig(target string) string {
243249
// Skip comments
244250
continue
245251
}
246-
fields := bytes.SplitN(line, []byte{'='}, 2)
247-
if len(fields) != 2 {
248-
continue
249-
}
250-
if bytes.Equal(fields[0], []byte(target)) {
251-
return string(bytes.Trim(fields[1], `"`))
252+
key, val, ok := bytes.Cut(line, []byte{'='})
253+
if ok && string(key) == target {
254+
return string(bytes.Trim(val, `"`))
252255
}
253256
}
254257
return ""
@@ -580,21 +583,20 @@ func catsToBitset(cats string) (*big.Int, error) {
580583

581584
catlist := strings.Split(cats, ",")
582585
for _, r := range catlist {
583-
ranges := strings.SplitN(r, ".", 2)
584-
if len(ranges) > 1 {
585-
catstart, err := parseLevelItem(ranges[0], category)
586+
if s, e, ok := strings.Cut(r, "."); ok {
587+
catstart, err := parseLevelItem(s, category)
586588
if err != nil {
587589
return nil, err
588590
}
589-
catend, err := parseLevelItem(ranges[1], category)
591+
catend, err := parseLevelItem(e, category)
590592
if err != nil {
591593
return nil, err
592594
}
593595
for i := catstart; i <= catend; i++ {
594596
bitset.SetBit(bitset, i, 1)
595597
}
596598
} else {
597-
cat, err := parseLevelItem(ranges[0], category)
599+
cat, err := parseLevelItem(r, category)
598600
if err != nil {
599601
return nil, err
600602
}
@@ -622,14 +624,14 @@ func parseLevelItem(s string, sep levelItem) (int, error) {
622624
// parseLevel fills a level from a string that contains
623625
// a sensitivity and categories
624626
func (l *level) parseLevel(levelStr string) error {
625-
lvl := strings.SplitN(levelStr, ":", 2)
626-
sens, err := parseLevelItem(lvl[0], sensitivity)
627+
s, c, ok := strings.Cut(levelStr, ":")
628+
sens, err := parseLevelItem(s, sensitivity)
627629
if err != nil {
628630
return fmt.Errorf("failed to parse sensitivity: %w", err)
629631
}
630632
l.sens = sens
631-
if len(lvl) > 1 {
632-
cats, err := catsToBitset(lvl[1])
633+
if ok {
634+
cats, err := catsToBitset(c)
633635
if err != nil {
634636
return fmt.Errorf("failed to parse categories: %w", err)
635637
}
@@ -642,25 +644,19 @@ func (l *level) parseLevel(levelStr string) error {
642644
// rangeStrToMLSRange marshals a string representation of a range.
643645
func rangeStrToMLSRange(rangeStr string) (*mlsRange, error) {
644646
r := &mlsRange{}
645-
l := strings.SplitN(rangeStr, "-", 2)
646-
647-
switch len(l) {
648-
// rangeStr that has a low and a high level, e.g. s4:c0.c1023-s6:c0.c1023
649-
case 2:
647+
lo, hi, ok := strings.Cut(rangeStr, "-")
648+
r.low = &level{}
649+
if err := r.low.parseLevel(lo); err != nil {
650+
return nil, fmt.Errorf("failed to parse low level %q: %w", lo, err)
651+
}
652+
if ok {
653+
// rangeStr that has a low and a high level, e.g. s4:c0.c1023-s6:c0.c1023.
650654
r.high = &level{}
651-
if err := r.high.parseLevel(l[1]); err != nil {
652-
return nil, fmt.Errorf("failed to parse high level %q: %w", l[1], err)
655+
if err := r.high.parseLevel(hi); err != nil {
656+
return nil, fmt.Errorf("failed to parse high level %q: %w", hi, err)
653657
}
654-
fallthrough
655-
// rangeStr that is single level, e.g. s6:c0,c3,c5,c30.c1023
656-
case 1:
657-
r.low = &level{}
658-
if err := r.low.parseLevel(l[0]); err != nil {
659-
return nil, fmt.Errorf("failed to parse low level %q: %w", l[0], err)
660-
}
661-
}
662-
663-
if r.high == nil {
658+
} else {
659+
// rangeStr that is single level, e.g. s6:c0,c3,c5,c30.c1023.
664660
r.high = r.low
665661
}
666662

@@ -872,8 +868,8 @@ func checkLabel(label string) error {
872868
if len(label) != 0 {
873869
con := strings.SplitN(label, ":", 4)
874870
if len(con) > 3 {
875-
state.Lock()
876-
defer state.Unlock()
871+
state.RLock()
872+
defer state.RUnlock()
877873
if state.mcsList[con[3]] {
878874
return ErrMCSAlreadyExists
879875
}
@@ -1020,12 +1016,10 @@ var loadLabels = sync.OnceValue(func() map[string]string {
10201016
// Skip comments
10211017
continue
10221018
}
1023-
fields := bytes.SplitN(line, []byte{'='}, 2)
1024-
if len(fields) != 2 {
1025-
continue
1019+
if key, val, ok := bytes.Cut(line, []byte{'='}); ok {
1020+
key, val = bytes.TrimSpace(key), bytes.TrimSpace(val)
1021+
labels[string(key)] = string(bytes.Trim(val, `"`))
10261022
}
1027-
key, val := bytes.TrimSpace(fields[0]), bytes.TrimSpace(fields[1])
1028-
labels[string(key)] = string(bytes.Trim(val, `"`))
10291023
}
10301024

10311025
con, _ := NewContext(labels["file"])

go-selinux/selinux_linux_test.go

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -131,32 +131,27 @@ func TestDuplicateLabel(t *testing.T) {
131131
t.Fatalf("DupSecOpt: %v", err)
132132
}
133133
for _, opt := range secopt {
134-
con := strings.SplitN(opt, ":", 2)
135-
if con[0] == "user" {
136-
if con[1] != "system_u" {
134+
key, val, _ := strings.Cut(opt, ":")
135+
switch key {
136+
case "user":
137+
if val != "system_u" {
137138
t.Errorf("DupSecOpt Failed user incorrect")
138139
}
139-
continue
140-
}
141-
if con[0] == "role" {
142-
if con[1] != "system_r" {
140+
case "role":
141+
if val != "system_r" {
143142
t.Errorf("DupSecOpt Failed role incorrect")
144143
}
145-
continue
146-
}
147-
if con[0] == "type" {
148-
if con[1] != "container_t" {
144+
case "type":
145+
if val != "container_t" {
149146
t.Errorf("DupSecOpt Failed type incorrect")
150147
}
151-
continue
152-
}
153-
if con[0] == "level" {
154-
if con[1] != "s0:c1,c2" {
148+
case "level":
149+
if val != "s0:c1,c2" {
155150
t.Errorf("DupSecOpt Failed level incorrect")
156151
}
157-
continue
152+
default:
153+
t.Errorf("DupSecOpt failed: invalid field %q", key)
158154
}
159-
t.Errorf("DupSecOpt failed: invalid field %q", con[0])
160155
}
161156
secopt = DisableSecOpt()
162157
if secopt[0] != "disable" {

0 commit comments

Comments
 (0)