Skip to content

Commit 936fe01

Browse files
committed
fix: uniqMcs use all cpu
Signed-off-by: ningmingxiao <ning.mingxiao@zte.com.cn>
1 parent bc9c180 commit 936fe01

8 files changed

Lines changed: 160 additions & 73 deletions

File tree

go-selinux/label/label_linux.go

Lines changed: 1 addition & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,11 @@ package label
22

33
import (
44
"errors"
5-
"fmt"
65
"strings"
76

87
"github.com/opencontainers/selinux/go-selinux"
98
)
109

11-
// Valid Label Options
12-
var validOptions = map[string]bool{
13-
"disable": true,
14-
"type": true,
15-
"filetype": true,
16-
"user": true,
17-
"role": true,
18-
"level": true,
19-
}
20-
2110
var ErrIncompatibleLabel = errors.New("bad SELinux option: z and Z can not be used together")
2211

2312
// InitLabels returns the process label and file labels to be used within
@@ -27,55 +16,7 @@ var ErrIncompatibleLabel = errors.New("bad SELinux option: z and Z can not be us
2716
// If the disabled flag is passed in, the process label will not be set, but the mount label will be set
2817
// to the container_file label with the maximum category. This label is not usable by any confined label.
2918
func InitLabels(options []string) (plabel string, mlabel string, retErr error) {
30-
if !selinux.GetEnabled() {
31-
return "", "", nil
32-
}
33-
processLabel, mountLabel := selinux.ContainerLabels()
34-
if processLabel == "" {
35-
// processLabel is required; if empty, do nothing.
36-
return processLabel, mountLabel, nil
37-
}
38-
defer func() {
39-
if retErr != nil {
40-
selinux.ReleaseLabel(mountLabel)
41-
}
42-
}()
43-
pcon, err := selinux.NewContext(processLabel)
44-
if err != nil {
45-
return "", "", err
46-
}
47-
mcsLevel := pcon["level"]
48-
mcon, err := selinux.NewContext(mountLabel)
49-
if err != nil {
50-
return "", "", err
51-
}
52-
for _, opt := range options {
53-
if opt == "disable" {
54-
selinux.ReleaseLabel(mountLabel)
55-
return "", selinux.PrivContainerMountLabel(), nil
56-
}
57-
k, v, ok := strings.Cut(opt, ":")
58-
if !ok || !validOptions[k] {
59-
return "", "", fmt.Errorf("bad label option %q, valid options 'disable' or \n'user, role, level, type, filetype' followed by ':' and a value", opt)
60-
}
61-
if k == "filetype" {
62-
mcon["type"] = v
63-
continue
64-
}
65-
pcon[k] = v
66-
if k == "level" || k == "user" {
67-
mcon[k] = v
68-
}
69-
}
70-
if p := pcon.Get(); p != processLabel {
71-
if pcon["level"] != mcsLevel {
72-
selinux.ReleaseLabel(processLabel)
73-
}
74-
selinux.ReserveLabel(p)
75-
processLabel = p
76-
}
77-
mountLabel = mcon.Get()
78-
return processLabel, mountLabel, nil
19+
return selinux.InitLabels(options)
7920
}
8021

8122
// SetFileLabel modifies the "path" label to the specified file label

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/labels.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package selinux
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
// Valid Label Options
9+
var validOptions = map[string]bool{
10+
"disable": true,
11+
"type": true,
12+
"filetype": true,
13+
"user": true,
14+
"role": true,
15+
"level": true,
16+
}
17+
18+
// InitLabels returns the process label and file labels to be used within
19+
// the container. A list of options can be passed into this function to alter
20+
// the labels. The labels returned will include a random MCS String, that is
21+
// guaranteed to be unique.
22+
// If the disabled flag is passed in, the process label will not be set, but the mount label will be set
23+
// to the container_file label with the maximum category. This label is not usable by any confined label.
24+
func InitLabels(options []string) (plabel string, mlabel string, retErr error) {
25+
if !GetEnabled() {
26+
return "", "", nil
27+
}
28+
if err := checkSELinuxLabelExhaustion(); err != nil {
29+
return "", "", err
30+
}
31+
processLabel, mountLabel := containerLabels()
32+
if processLabel == "" {
33+
// processLabel is required; if empty, do nothing.
34+
return processLabel, mountLabel, nil
35+
}
36+
defer func() {
37+
if retErr != nil {
38+
ReleaseLabel(mountLabel)
39+
}
40+
}()
41+
pcon, err := NewContext(processLabel)
42+
if err != nil {
43+
return "", "", err
44+
}
45+
mcsLevel := pcon["level"]
46+
mcon, err := NewContext(mountLabel)
47+
if err != nil {
48+
return "", "", err
49+
}
50+
for _, opt := range options {
51+
if opt == "disable" {
52+
ReleaseLabel(mountLabel)
53+
return "", PrivContainerMountLabel(), nil
54+
}
55+
k, v, ok := strings.Cut(opt, ":")
56+
if !ok || !validOptions[k] {
57+
return "", "", fmt.Errorf("bad label option %q, valid options 'disable' or \n'user, role, level, type, filetype' followed by ':' and a value", opt)
58+
}
59+
if k == "filetype" {
60+
mcon["type"] = v
61+
continue
62+
}
63+
pcon[k] = v
64+
if k == "level" || k == "user" {
65+
mcon[k] = v
66+
}
67+
}
68+
if p := pcon.Get(); p != processLabel {
69+
if pcon["level"] != mcsLevel {
70+
ReleaseLabel(processLabel)
71+
}
72+
ReserveLabel(p)
73+
processLabel = p
74+
}
75+
mountLabel = mcon.Get()
76+
return processLabel, mountLabel, nil
77+
}

go-selinux/selinux.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -262,20 +262,38 @@ func ROFileLabel() string {
262262

263263
// KVMContainerLabels returns the default processLabel and mountLabel to be used
264264
// for kvm containers by the calling process.
265+
//
266+
// Deprecated: Use KVMContainerLabel instead.
265267
func KVMContainerLabels() (string, string) {
266268
return kvmContainerLabels()
267269
}
268270

271+
// KVMContainerLabel returns the default processLabel and mountLabel to be used
272+
// for kvm containers by the calling process.
273+
func KVMContainerLabel() (processLabel, fileLabel string, err error) {
274+
if err = checkSELinuxLabelExhaustion(); err != nil {
275+
return "", "", err
276+
}
277+
processLabel, fileLabel = kvmContainerLabels()
278+
return processLabel, fileLabel, nil
279+
}
280+
269281
// InitContainerLabels returns the default processLabel and file labels to be
270282
// used for containers running an init system like systemd by the calling process.
283+
//
284+
// Deprecated: Use InitContainerLabel instead.
271285
func InitContainerLabels() (string, string) {
272286
return initContainerLabels()
273287
}
274288

275-
// ContainerLabels returns an allocated processLabel and fileLabel to be used for
276-
// container labeling by the calling process.
277-
func ContainerLabels() (processLabel string, fileLabel string) {
278-
return containerLabels()
289+
// InitContainerLabel returns the default processLabel and file labels to be
290+
// used for containers running an init system like systemd by the calling process.
291+
func InitContainerLabel() (processLabel, fileLabel string, err error) {
292+
if err = checkSELinuxLabelExhaustion(); err != nil {
293+
return "", "", err
294+
}
295+
processLabel, fileLabel = initContainerLabels()
296+
return processLabel, fileLabel, nil
279297
}
280298

281299
// SecurityCheckContext validates that the SELinux label is understood by the kernel

go-selinux/selinux_linux.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ const (
3838
xattrNameSelinux = "security.selinux"
3939
)
4040

41+
var maxSelinuxLabelSize = int(CategoryRange * (CategoryRange - 1) / 2)
42+
4143
type selinuxState struct {
4244
mcsList map[string]bool
4345
enabledSet bool
4446
enabled bool
45-
sync.Mutex
47+
sync.RWMutex
4648
}
4749

4850
type level struct {
@@ -90,6 +92,12 @@ var policyRoot = sync.OnceValue(func() string {
9092
return filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
9193
})
9294

95+
func containerLabelsSize() int {
96+
state.RLock()
97+
defer state.RUnlock()
98+
return len(state.mcsList)
99+
}
100+
93101
func (s *selinuxState) setEnable(enabled bool) bool {
94102
s.Lock()
95103
defer s.Unlock()
@@ -848,8 +856,8 @@ func checkLabel(label string) error {
848856
if len(label) != 0 {
849857
con := strings.SplitN(label, ":", 4)
850858
if len(con) > 3 {
851-
state.Lock()
852-
defer state.Unlock()
859+
state.RLock()
860+
defer state.RUnlock()
853861
if state.mcsList[con[3]] {
854862
return ErrMCSAlreadyExists
855863
}
@@ -1035,6 +1043,13 @@ func initContainerLabels() (string, string) {
10351043
return addMcs(processLabel, label("file"))
10361044
}
10371045

1046+
func checkSELinuxLabelExhaustion() error {
1047+
if size := containerLabelsSize(); size >= maxSelinuxLabelSize {
1048+
return fmt.Errorf("SELinux label exhaustion: %d labels used", size)
1049+
}
1050+
return nil
1051+
}
1052+
10381053
// containerLabels returns an allocated processLabel and fileLabel to be used for
10391054
// container labeling by the calling process.
10401055
func containerLabels() (processLabel string, fileLabel string) {

go-selinux/selinux_linux_test.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,10 @@ func TestInitLabels(t *testing.T) {
110110
t.Skip("SELinux not enabled, skipping.")
111111
}
112112

113-
plabel, flabel := InitContainerLabels()
113+
plabel, flabel, err := InitContainerLabel()
114+
if err != nil {
115+
t.Fatal(err)
116+
}
114117
if plabel == "" {
115118
t.Log("Failed to read init label")
116119
}
@@ -291,20 +294,32 @@ func TestSELinux(t *testing.T) {
291294
plabel, flabel string
292295
)
293296

294-
plabel, flabel = ContainerLabels()
297+
plabel, flabel, err = InitLabels([]string{})
298+
if err != nil {
299+
t.Fatal("InitLabels failed:", err)
300+
}
295301
t.Log(plabel)
296302
t.Log(flabel)
297-
plabel, flabel = ContainerLabels()
303+
plabel, flabel, err = InitLabels([]string{})
304+
if err != nil {
305+
t.Fatal("InitLabels failed:", err)
306+
}
298307
t.Log(plabel)
299308
t.Log(flabel)
300309
ReleaseLabel(plabel)
301310

302-
plabel, flabel = ContainerLabels()
311+
plabel, flabel, err = InitLabels([]string{})
312+
if err != nil {
313+
t.Fatal("InitLabels failed:", err)
314+
}
303315
t.Log(plabel)
304316
t.Log(flabel)
305317
ClearLabels()
306318
t.Log("ClearLabels")
307-
plabel, flabel = ContainerLabels()
319+
plabel, flabel, err = InitLabels([]string{})
320+
if err != nil {
321+
t.Fatal("InitLabels failed:", err)
322+
}
308323
t.Log(plabel)
309324
t.Log(flabel)
310325
ReleaseLabel(plabel)

go-selinux/selinux_stub.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ func containerLabels() (string, string) {
134134
return "", ""
135135
}
136136

137+
func checkSELinuxLabelExhaustion() error {
138+
return nil
139+
}
140+
137141
func securityCheckContext(string) error {
138142
return nil
139143
}

go-selinux/selinux_stub_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func TestSELinuxStubs(t *testing.T) {
115115
if v := ROFileLabel(); v != "" {
116116
t.Errorf(`expected "", got %q`, v)
117117
}
118-
if processLbl, fileLbl := ContainerLabels(); processLbl != "" || fileLbl != "" {
118+
if processLbl, fileLbl, err := InitLabels([]string{}); err == nil && (processLbl != "" || fileLbl != "") {
119119
t.Errorf(`expected fileLbl="", fileLbl="" got processLbl=%q, fileLbl=%q`, processLbl, fileLbl)
120120
}
121121
if err = SecurityCheckContext(testLabel); err != nil {

0 commit comments

Comments
 (0)