mirror of
https://github.com/opencontainers/runc.git
synced 2025-09-27 03:46:19 +08:00

Before commit06f1e0765
("libct: speedup process.Env handling") we were overriding HOME if it was set to "" too[1]. But now we only override it if it wasn't set at all. This patch restores the old behavior of overriding it if it was set to an empty value. Docker relies on this behaviour since ages[2]. [1]:1c50804572/libcontainer/init_linux.go (L544-L549)
[2]:843e51459f/integration-cli/docker_cli_run_test.go (L822-L843)
Signed-off-by: Rodrigo Campos <rodrigoca@microsoft.com>
101 lines
2.6 KiB
Go
101 lines
2.6 KiB
Go
package libcontainer
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/moby/sys/user"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// prepareEnv processes a list of environment variables, preparing it
|
|
// for direct consumption by unix.Exec. In particular, it:
|
|
// - validates each variable is in the NAME=VALUE format and
|
|
// contains no \0 (nil) bytes;
|
|
// - removes any duplicates (keeping only the last value for each key)
|
|
// - sets PATH for the current process, if found in the list;
|
|
// - adds HOME to returned environment, if not found in the list,
|
|
// or the value is empty.
|
|
//
|
|
// Returns the prepared environment.
|
|
func prepareEnv(env []string, uid int) ([]string, error) {
|
|
if env == nil {
|
|
return nil, nil
|
|
}
|
|
var homeIsSet bool
|
|
|
|
// Deduplication code based on dedupEnv from Go 1.22 os/exec.
|
|
|
|
// Construct the output in reverse order, to preserve the
|
|
// last occurrence of each key.
|
|
out := make([]string, 0, len(env))
|
|
saw := make(map[string]bool, len(env))
|
|
for n := len(env); n > 0; n-- {
|
|
kv := env[n-1]
|
|
i := strings.IndexByte(kv, '=')
|
|
if i == -1 {
|
|
return nil, errors.New("invalid environment variable: missing '='")
|
|
}
|
|
if i == 0 {
|
|
return nil, errors.New("invalid environment variable: name cannot be empty")
|
|
}
|
|
key := kv[:i]
|
|
val := kv[i+1:]
|
|
if saw[key] { // Duplicate.
|
|
continue
|
|
}
|
|
saw[key] = true
|
|
if strings.IndexByte(kv, 0) >= 0 {
|
|
return nil, fmt.Errorf("invalid environment variable %q: contains nul byte (\\x00)", key)
|
|
}
|
|
if key == "PATH" {
|
|
// Needs to be set as it is used for binary lookup.
|
|
if err := os.Setenv("PATH", val); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if key == "HOME" {
|
|
if val != "" {
|
|
homeIsSet = true
|
|
} else {
|
|
// Don't add empty HOME to the environment, we will override it later.
|
|
continue
|
|
}
|
|
}
|
|
out = append(out, kv)
|
|
}
|
|
// Restore the original order.
|
|
slices.Reverse(out)
|
|
|
|
// If HOME is not found in env, get it from container's /etc/passwd and add.
|
|
if !homeIsSet {
|
|
home, err := getUserHome(uid)
|
|
if err != nil {
|
|
// For backward compatibility, don't return an error, but merely log it.
|
|
logrus.WithError(err).Debugf("HOME not set in process.env, and getting UID %d homedir failed", uid)
|
|
}
|
|
|
|
out = append(out, "HOME="+home)
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func getUserHome(uid int) (string, error) {
|
|
const defaultHome = "/" // Default value, return this with any error.
|
|
|
|
u, err := user.LookupUid(uid)
|
|
if err != nil {
|
|
// ErrNoPasswdEntries is kinda expected as any UID can be specified.
|
|
if errors.Is(err, user.ErrNoPasswdEntries) {
|
|
err = nil
|
|
}
|
|
return defaultHome, err
|
|
}
|
|
|
|
return u.Home, nil
|
|
}
|