Skip to content

Commit 41bad43

Browse files
committed
Add support for injecting additional GIDs
This change adds support for injecting additional GIDs using CDI. This is on by default in cases that use the internal representation and can be opted out of by enabling the `no-additional-gids` feature. Signed-off-by: Evan Lezar <[email protected]>
1 parent af19c52 commit 41bad43

File tree

13 files changed

+118
-28
lines changed

13 files changed

+118
-28
lines changed

internal/config/features.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,20 @@ package config
1919
type featureName string
2020

2121
const (
22-
FeatureGDS = featureName("gds")
23-
FeatureMOFED = featureName("mofed")
24-
FeatureNVSWITCH = featureName("nvswitch")
25-
FeatureGDRCopy = featureName("gdrcopy")
22+
FeatureGDS = featureName("gds")
23+
FeatureMOFED = featureName("mofed")
24+
FeatureNVSWITCH = featureName("nvswitch")
25+
FeatureGDRCopy = featureName("gdrcopy")
26+
FeatureNoAdditionalGIDs = featureName("no-additional-gids")
2627
)
2728

2829
// features specifies a set of named features.
2930
type features struct {
30-
GDS *feature `toml:"gds,omitempty"`
31-
MOFED *feature `toml:"mofed,omitempty"`
32-
NVSWITCH *feature `toml:"nvswitch,omitempty"`
33-
GDRCopy *feature `toml:"gdrcopy,omitempty"`
31+
GDS *feature `toml:"gds,omitempty"`
32+
MOFED *feature `toml:"mofed,omitempty"`
33+
NVSWITCH *feature `toml:"nvswitch,omitempty"`
34+
GDRCopy *feature `toml:"gdrcopy,omitempty"`
35+
NoAdditionalGIDs *feature `toml:"no-additional-gids,omitempty"`
3436
}
3537

3638
type feature bool
@@ -40,10 +42,11 @@ type feature bool
4042
// variables can also be supplied.
4143
func (fs features) IsEnabled(n featureName, in ...getenver) bool {
4244
featureEnvvars := map[featureName]string{
43-
FeatureGDS: "NVIDIA_GDS",
44-
FeatureMOFED: "NVIDIA_MOFED",
45-
FeatureNVSWITCH: "NVIDIA_NVSWITCH",
46-
FeatureGDRCopy: "NVIDIA_GDRCOPY",
45+
FeatureGDS: "NVIDIA_GDS",
46+
FeatureMOFED: "NVIDIA_MOFED",
47+
FeatureNVSWITCH: "NVIDIA_NVSWITCH",
48+
FeatureGDRCopy: "NVIDIA_GDRCOPY",
49+
FeatureNoAdditionalGIDs: "NVIDIA_NO_ADDITIONAL_GIDS",
4750
}
4851

4952
envvar := featureEnvvars[n]
@@ -56,6 +59,8 @@ func (fs features) IsEnabled(n featureName, in ...getenver) bool {
5659
return fs.NVSWITCH.isEnabled(envvar, in...)
5760
case FeatureGDRCopy:
5861
return fs.GDRCopy.isEnabled(envvar, in...)
62+
case FeatureNoAdditionalGIDs:
63+
return fs.NoAdditionalGIDs.isEnabled(envvar, in...)
5964
default:
6065
return false
6166
}

internal/edits/device.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
package edits
1818

1919
import (
20+
"os"
21+
22+
"golang.org/x/sys/unix"
2023
"tags.cncf.io/container-device-interface/pkg/cdi"
2124
"tags.cncf.io/container-device-interface/specs-go"
2225

@@ -26,15 +29,23 @@ import (
2629
type device discover.Device
2730

2831
// toEdits converts a discovered device to CDI Container Edits.
29-
func (d device) toEdits() (*cdi.ContainerEdits, error) {
32+
func (d device) toEdits(allowAdditionalGIDs bool) (*cdi.ContainerEdits, error) {
3033
deviceNode, err := d.toSpec()
3134
if err != nil {
3235
return nil, err
3336
}
3437

38+
var additionalGIDs []uint32
39+
if allowAdditionalGIDs {
40+
if requiredGID, _ := d.getRequiredGID(); requiredGID != 0 {
41+
additionalGIDs = append(additionalGIDs, requiredGID)
42+
}
43+
}
44+
3545
e := cdi.ContainerEdits{
3646
ContainerEdits: &specs.ContainerEdits{
37-
DeviceNodes: []*specs.DeviceNode{deviceNode},
47+
DeviceNodes: []*specs.DeviceNode{deviceNode},
48+
AdditionalGIDs: additionalGIDs,
3849
},
3950
}
4051
return &e, nil
@@ -52,10 +63,37 @@ func (d device) toSpec() (*specs.DeviceNode, error) {
5263
if hostPath == d.Path {
5364
hostPath = ""
5465
}
66+
5567
s := specs.DeviceNode{
5668
HostPath: hostPath,
5769
Path: d.Path,
5870
}
5971

6072
return &s, nil
6173
}
74+
75+
// getRequiredGID returns the group id of the device if the device is not world read/writable
76+
func (d device) getRequiredGID() (uint32, error) {
77+
path := d.HostPath
78+
if path == "" {
79+
path = d.Path
80+
}
81+
if path == "" {
82+
return 0, nil
83+
}
84+
85+
var stat unix.Stat_t
86+
if err := unix.Lstat(path, &stat); err != nil {
87+
return 0, err
88+
}
89+
// This is only supported for char devices
90+
if stat.Mode&unix.S_IFMT != unix.S_IFCHR {
91+
return 0, nil
92+
}
93+
94+
permissions := os.FileMode(stat.Mode).Perm()
95+
if permissions&06 == 0 {
96+
return stat.Gid, nil
97+
}
98+
return 0, nil
99+
}

internal/edits/lib.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (o *options) EditsFromDiscoverer(d discover.Discover) (*cdi.ContainerEdits,
5858

5959
c := NewContainerEdits()
6060
for _, d := range devices {
61-
edits, err := device(d).toEdits()
61+
edits, err := device(d).toEdits(o.allowAdditionalGIDs)
6262
if err != nil {
6363
return nil, fmt.Errorf("failed to created container edits for device: %v", err)
6464
}

internal/edits/options.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ package edits
1919
import "github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
2020

2121
type options struct {
22-
logger logger.Interface
22+
logger logger.Interface
23+
allowAdditionalGIDs bool
2324
}
2425

2526
type Option func(*options)
@@ -29,3 +30,9 @@ func WithLogger(logger logger.Interface) Option {
2930
o.logger = logger
3031
}
3132
}
33+
34+
func WithAllowAdditionalGIDs(allowAdditionalGIDs bool) Option {
35+
return func(o *options) {
36+
o.allowAdditionalGIDs = allowAdditionalGIDs
37+
}
38+
}

internal/modifier/discover.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,18 @@ import (
2828
)
2929

3030
type discoverModifier struct {
31-
logger logger.Interface
32-
discoverer discover.Discover
31+
logger logger.Interface
32+
discoverer discover.Discover
33+
allowAdditionalGIDs bool
3334
}
3435

3536
// NewModifierFromDiscoverer creates a modifier that applies the discovered
3637
// modifications to an OCI spec if required by the runtime wrapper.
37-
func NewModifierFromDiscoverer(logger logger.Interface, d discover.Discover) (oci.SpecModifier, error) {
38+
func NewModifierFromDiscoverer(logger logger.Interface, d discover.Discover, allowAdditionalGIDs bool) (oci.SpecModifier, error) {
3839
m := discoverModifier{
39-
logger: logger,
40-
discoverer: d,
40+
logger: logger,
41+
discoverer: d,
42+
allowAdditionalGIDs: allowAdditionalGIDs,
4143
}
4244
return &m, nil
4345
}
@@ -47,6 +49,7 @@ func NewModifierFromDiscoverer(logger logger.Interface, d discover.Discover) (oc
4749
func (m discoverModifier) Modify(spec *specs.Spec) error {
4850
e := edits.New(
4951
edits.WithLogger(m.logger),
52+
edits.WithAllowAdditionalGIDs(m.allowAdditionalGIDs),
5053
)
5154

5255
specEdits, err := e.SpecModifierFromDiscoverer(m.discoverer)

internal/modifier/discover_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func TestDiscoverModifier(t *testing.T) {
132132

133133
for _, tc := range testCases {
134134
t.Run(tc.description, func(t *testing.T) {
135-
m, err := NewModifierFromDiscoverer(logger, tc.discover)
135+
m, err := NewModifierFromDiscoverer(logger, tc.discover, true)
136136
require.NoError(t, err)
137137

138138
err = m.Modify(tc.spec)

internal/modifier/gated.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,7 @@ func NewFeatureGatedModifier(logger logger.Interface, cfg *config.Config, image
7878
discoverers = append(discoverers, d)
7979
}
8080

81-
return NewModifierFromDiscoverer(logger, discover.Merge(discoverers...))
81+
allowAdditionalGIDs := !cfg.Features.IsEnabled(config.FeatureNoAdditionalGIDs, image)
82+
83+
return NewModifierFromDiscoverer(logger, discover.Merge(discoverers...), allowAdditionalGIDs)
8284
}

internal/modifier/graphics.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ func NewGraphicsModifier(logger logger.Interface, cfg *config.Config, image imag
6262
drmNodes,
6363
mounts,
6464
)
65-
return NewModifierFromDiscoverer(logger, d)
65+
66+
allowAdditionalGIDs := !cfg.Features.IsEnabled(config.FeatureNoAdditionalGIDs, image)
67+
return NewModifierFromDiscoverer(logger, d, allowAdditionalGIDs)
6668
}
6769

6870
// requiresGraphicsModifier determines whether a graphics modifier is required.

internal/oci/spec_mock.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/nvcdi/lib.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ type nvcdilib struct {
6565
infolib info.Interface
6666

6767
mergedDeviceOptions []transform.MergedDeviceOption
68+
69+
allowAdditionalGIDs bool
6870
}
6971

7072
// New creates a new nvcdi library
@@ -196,6 +198,7 @@ func (m *wrapper) GetCommonEdits() (*cdi.ContainerEdits, error) {
196198
func (l *nvcdilib) editsFromDiscoverer(d discover.Discover) (*cdi.ContainerEdits, error) {
197199
e := edits.New(
198200
edits.WithLogger(l.logger),
201+
edits.WithAllowAdditionalGIDs(l.allowAdditionalGIDs),
199202
)
200203
return e.EditsFromDiscoverer(d)
201204
}

pkg/nvcdi/namer_nvml_mock.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/nvcdi/options.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,11 @@ func WithLibrarySearchPaths(paths []string) Option {
155155
o.librarySearchPaths = paths
156156
}
157157
}
158+
159+
// WithAllowAdditionalGIDs specifies whether a generated CDI spec can contain
160+
// the additionaGIDs field.
161+
func WithAllowAdditionalGIDs(allowAdditionalGIDs bool) Option {
162+
return func(o *nvcdilib) {
163+
o.allowAdditionalGIDs = allowAdditionalGIDs
164+
}
165+
}

pkg/nvcdi/transform/deduplicate.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package transform
1818

1919
import (
20+
"slices"
21+
2022
"tags.cncf.io/container-device-interface/specs-go"
2123
)
2224

@@ -50,6 +52,12 @@ func (d dedupe) Transform(spec *specs.Spec) error {
5052
}
5153

5254
func (d dedupe) transformEdits(edits *specs.ContainerEdits) error {
55+
additionalGIDs, err := d.deduplicateAdditionalGIDs(edits.AdditionalGIDs)
56+
if err != nil {
57+
return err
58+
}
59+
edits.AdditionalGIDs = additionalGIDs
60+
5361
deviceNodes, err := d.deduplicateDeviceNodes(edits.DeviceNodes)
5462
if err != nil {
5563
return err
@@ -150,3 +158,19 @@ func (d dedupe) deduplicateMounts(entities []*specs.Mount) ([]*specs.Mount, erro
150158
}
151159
return mounts, nil
152160
}
161+
162+
func (d dedupe) deduplicateAdditionalGIDs(entities []uint32) ([]uint32, error) {
163+
seen := make(map[uint32]bool)
164+
var additionalGIDs []uint32
165+
for _, e := range entities {
166+
if seen[e] {
167+
continue
168+
}
169+
seen[e] = true
170+
additionalGIDs = append(additionalGIDs, e)
171+
}
172+
173+
slices.Sort(additionalGIDs)
174+
175+
return additionalGIDs, nil
176+
}

0 commit comments

Comments
 (0)