Skip to content

Commit 5d16bae

Browse files
committed
feat: Introduce shell metacharacter escaping for exec
Signed-off-by: Nathanael Bracy <nate@bracy.dev>
1 parent 973a663 commit 5d16bae

File tree

5 files changed

+42
-10
lines changed

5 files changed

+42
-10
lines changed

cmd/nvidia-container-runtime-hook/main.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/NVIDIA/nvidia-container-toolkit/internal/info"
1818
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
1919
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup"
20+
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
2021
)
2122

2223
var (
@@ -143,10 +144,10 @@ func doPrestart() {
143144

144145
args = append(args, fmt.Sprintf("--pid=%s", strconv.FormatUint(uint64(container.Pid), 10)))
145146
args = append(args, rootfs)
147+
args = oci.Escape(args)
146148

147149
env := append(os.Environ(), cli.Environment...)
148-
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection?
149-
err = syscall.Exec(args[0], args, env)
150+
err = syscall.Exec(args[0], args, env) //nolint:gosec
150151
log.Panicln("exec failed:", err)
151152
}
152153

cmd/nvidia-ctk/hook/update-ldcache/update-ldcache.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ func (m command) run(c *cli.Context, cfg *options) error {
131131
// Explicitly specify using /etc/ld.so.conf since the host's ldconfig may
132132
// be configured to use a different config file by default.
133133
args = append(args, "-f", "/etc/ld.so.conf")
134+
args = oci.Escape(args)
134135

135-
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
136-
return syscall.Exec(ldconfigPath, args, nil)
136+
return syscall.Exec(ldconfigPath, args, nil) //nolint:gosec
137137
}
138138

139139
type root string

internal/oci/runtime_syscall_exec.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,47 @@ package oci
1919
import (
2020
"fmt"
2121
"os"
22+
"regexp"
23+
"strings"
2224
"syscall"
2325
)
2426

27+
// shellMetachars represents a set of shell metacharacters that are commonly
28+
// used for shell scripting and may lead to security vulnerabilities if not
29+
// properly handled.
30+
//
31+
// These metacharacters include: | & ; ( ) < > \t \n $ \ `
32+
const shellMetachars = "|&;()<> \t\n$\\`"
33+
2534
type syscallExec struct{}
2635

2736
var _ Runtime = (*syscallExec)(nil)
2837

38+
// Escape1 escapes shell metacharacters in a single command-line argument.
39+
func Escape1(arg string) string {
40+
if strings.ContainsAny(arg, shellMetachars) {
41+
// Argument contains shell metacharacters. Double quote the
42+
// argument, and backslash-escape any characters that still have
43+
// meaning inside of double quotes.
44+
e := regexp.MustCompile("([$`\"\\\\])").ReplaceAllString(arg, `\$1`)
45+
return fmt.Sprintf(`"%s"`, e)
46+
}
47+
return arg
48+
}
49+
50+
// Escape escapes shell metacharacters in a slice of command-line arguments
51+
// and returns a new slice containing the escaped arguments.
52+
func Escape(args []string) []string {
53+
escaped := make([]string, len(args))
54+
for i := range args {
55+
escaped[i] = Escape1(args[i])
56+
}
57+
return escaped
58+
}
59+
2960
func (r syscallExec) Exec(args []string) error {
30-
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
31-
err := syscall.Exec(args[0], args, os.Environ())
61+
args = Escape(args)
62+
err := syscall.Exec(args[0], args, os.Environ()) //nolint:gosec
3263
if err != nil {
3364
return fmt.Errorf("could not exec '%v': %v", args[0], err)
3465
}

tools/container/container.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/sirupsen/logrus"
2525
"github.com/urfave/cli/v2"
2626

27+
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
2728
"github.com/NVIDIA/nvidia-container-toolkit/pkg/config/engine"
2829
"github.com/NVIDIA/nvidia-container-toolkit/tools/container/operator"
2930
)
@@ -155,11 +156,11 @@ func (o Options) SystemdRestart(service string) error {
155156
args = append(args, "chroot", o.HostRootMount)
156157
}
157158
args = append(args, "systemctl", "restart", service)
159+
args = oci.Escape(args)
158160

159161
logrus.Infof("Restarting %v%v using systemd: %v", service, msg, args)
160162

161-
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
162-
cmd := exec.Command(args[0], args[1:]...)
163+
cmd := exec.Command(args[0], args[1:]...) //nolint:gosec
163164
cmd.Stdout = os.Stdout
164165
cmd.Stderr = os.Stderr
165166
err := cmd.Run()

tools/container/nvidia-toolkit/run.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,7 @@ func installToolkit(o *options) error {
229229
filepath.Join(o.root, toolkitSubDir),
230230
}
231231

232-
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
233-
cmd := exec.Command("sh", "-c", strings.Join(cmdline, " "))
232+
cmd := exec.Command("sh", "-c", strings.Join(cmdline, " ")) //nolint:gosec
234233
cmd.Stdout = os.Stdout
235234
cmd.Stderr = os.Stderr
236235
err := cmd.Run()

0 commit comments

Comments
 (0)