Skip to content

Commit ed053a7

Browse files
committed
nsenter: specify namespace type in setns()
This avoids us from running into cases where libcontainer thinks that a particular namespace file is a different type, and makes it a fatal error rather than causing broken functionality. Signed-off-by: Aleksa Sarai <[email protected]>
1 parent 02f8fa7 commit ed053a7

File tree

4 files changed

+123
-27
lines changed

4 files changed

+123
-27
lines changed

libcontainer/configs/namespaces_unix.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ var (
2222
supportedNamespaces = make(map[NamespaceType]bool)
2323
)
2424

25-
// nsToFile converts the namespace type to its filename
26-
func nsToFile(ns NamespaceType) string {
25+
// NsName converts the namespace type to its filename
26+
func NsName(ns NamespaceType) string {
2727
switch ns {
2828
case NEWNET:
2929
return "net"
@@ -50,7 +50,7 @@ func IsNamespaceSupported(ns NamespaceType) bool {
5050
if ok {
5151
return supported
5252
}
53-
nsFile := nsToFile(ns)
53+
nsFile := NsName(ns)
5454
// if the namespace type is unknown, just return false
5555
if nsFile == "" {
5656
return false
@@ -84,7 +84,7 @@ func (n *Namespace) GetPath(pid int) string {
8484
if n.Path != "" {
8585
return n.Path
8686
}
87-
return fmt.Sprintf("/proc/%d/ns/%s", pid, nsToFile(n.Type))
87+
return fmt.Sprintf("/proc/%d/ns/%s", pid, NsName(n.Type))
8888
}
8989

9090
func (n *Namespaces) Remove(t NamespaceType) bool {

libcontainer/container_linux.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,16 +1223,21 @@ func (c *linuxContainer) currentState() (*State, error) {
12231223
// can setns in order.
12241224
func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceType]string) ([]string, error) {
12251225
paths := []string{}
1226-
nsTypes := []configs.NamespaceType{
1226+
order := []configs.NamespaceType{
12271227
configs.NEWIPC,
12281228
configs.NEWUTS,
12291229
configs.NEWNET,
12301230
configs.NEWPID,
12311231
configs.NEWNS,
1232+
configs.NEWUSER,
12321233
}
1233-
// join userns if the init process explicitly requires NEWUSER
1234-
if c.config.Namespaces.Contains(configs.NEWUSER) {
1235-
nsTypes = append(nsTypes, configs.NEWUSER)
1234+
1235+
// Remove namespaces that we don't need to join.
1236+
var nsTypes []configs.NamespaceType
1237+
for _, ns := range order {
1238+
if c.config.Namespaces.Contains(ns) {
1239+
nsTypes = append(nsTypes, ns)
1240+
}
12361241
}
12371242
for _, nsType := range nsTypes {
12381243
if p, ok := namespaces[nsType]; ok && p != "" {
@@ -1249,7 +1254,7 @@ func (c *linuxContainer) orderNamespacePaths(namespaces map[configs.NamespaceTyp
12491254
if strings.ContainsRune(p, ',') {
12501255
return nil, newSystemError(fmt.Errorf("invalid path %s", p))
12511256
}
1252-
paths = append(paths, p)
1257+
paths = append(paths, fmt.Sprintf("%s:%s", configs.NsName(nsType), p))
12531258
}
12541259
}
12551260
return paths, nil

libcontainer/nsenter/nsenter_test.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func TestNsenterValidPaths(t *testing.T) {
2929

3030
namespaces := []string{
3131
// join pid ns of the current process
32-
fmt.Sprintf("/proc/%d/ns/pid", os.Getpid()),
32+
fmt.Sprintf("pid:/proc/%d/ns/pid", os.Getpid()),
3333
}
3434
cmd := &exec.Cmd{
3535
Path: os.Args[0],
@@ -87,7 +87,47 @@ func TestNsenterInvalidPaths(t *testing.T) {
8787

8888
namespaces := []string{
8989
// join pid ns of the current process
90-
fmt.Sprintf("/proc/%d/ns/pid", -1),
90+
fmt.Sprintf("pid:/proc/%d/ns/pid", -1),
91+
}
92+
cmd := &exec.Cmd{
93+
Path: os.Args[0],
94+
Args: args,
95+
ExtraFiles: []*os.File{child},
96+
Env: []string{"_LIBCONTAINER_INITPIPE=3"},
97+
}
98+
99+
if err := cmd.Start(); err != nil {
100+
t.Fatal(err)
101+
}
102+
// write cloneFlags
103+
r := nl.NewNetlinkRequest(int(libcontainer.InitMsg), 0)
104+
r.AddData(&libcontainer.Int32msg{
105+
Type: libcontainer.CloneFlagsAttr,
106+
Value: uint32(syscall.CLONE_NEWNET),
107+
})
108+
r.AddData(&libcontainer.Bytemsg{
109+
Type: libcontainer.NsPathsAttr,
110+
Value: []byte(strings.Join(namespaces, ",")),
111+
})
112+
if _, err := io.Copy(parent, bytes.NewReader(r.Serialize())); err != nil {
113+
t.Fatal(err)
114+
}
115+
116+
if err := cmd.Wait(); err == nil {
117+
t.Fatalf("nsenter exits with a zero exit status")
118+
}
119+
}
120+
121+
func TestNsenterIncorrectPathType(t *testing.T) {
122+
args := []string{"nsenter-exec"}
123+
parent, child, err := newPipe()
124+
if err != nil {
125+
t.Fatalf("failed to create pipe %v", err)
126+
}
127+
128+
namespaces := []string{
129+
// join pid ns of the current process
130+
fmt.Sprintf("net:/proc/%d/ns/pid", os.Getpid()),
91131
}
92132
cmd := &exec.Cmd{
93133
Path: os.Args[0],

libcontainer/nsenter/nsexec.c

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <stdint.h>
1212
#include <stdio.h>
1313
#include <stdlib.h>
14+
#include <stdbool.h>
1415
#include <string.h>
1516
#include <unistd.h>
1617

@@ -265,6 +266,44 @@ static void start_child(int pipenum, jmp_buf *env, int syncpipe[2], struct nlcon
265266
exit(0);
266267
}
267268

269+
/* Returns the clone(2) flag for a namespace, given the name of a namespace. */
270+
static int nsflag(char *name)
271+
{
272+
if (false)
273+
/* dummy */ ;
274+
#ifdef CLONE_NEWCGROUP
275+
else if (!strcmp(name, "cgroup"))
276+
return CLONE_NEWCGROUP;
277+
#endif
278+
#ifdef CLONE_NEWIPC
279+
else if (!strcmp(name, "ipc"))
280+
return CLONE_NEWIPC;
281+
#endif
282+
#ifdef CLONE_NEWNS
283+
else if (!strcmp(name, "mnt"))
284+
return CLONE_NEWNS;
285+
#endif
286+
#ifdef CLONE_NEWNET
287+
else if (!strcmp(name, "net"))
288+
return CLONE_NEWNET;
289+
#endif
290+
#ifdef CLONE_NEWPID
291+
else if (!strcmp(name, "pid"))
292+
return CLONE_NEWPID;
293+
#endif
294+
#ifdef CLONE_NEWUSER
295+
else if (!strcmp(name, "user"))
296+
return CLONE_NEWUSER;
297+
#endif
298+
#ifdef CLONE_NEWUTS
299+
else if (!strcmp(name, "uts"))
300+
return CLONE_NEWUTS;
301+
#endif
302+
303+
/* If we don't recognise a name, fallback to 0. */
304+
return 0;
305+
}
306+
268307
static void nl_parse(int fd, struct nlconfig_t *config)
269308
{
270309
size_t len, size;
@@ -328,8 +367,13 @@ static void nl_parse(int fd, struct nlconfig_t *config)
328367
*/
329368
char *saveptr = NULL;
330369
char *ns = strtok_r(current, ",", &saveptr);
331-
int *fds = NULL, num = 0, i;
332-
char **paths = NULL;
370+
int num = 0, i;
371+
372+
struct namespace_t {
373+
int fd;
374+
int ns;
375+
char *path;
376+
} *nses = NULL;
333377

334378
if (!ns || !strlen(current))
335379
bail("ns paths are empty");
@@ -341,32 +385,39 @@ static void nl_parse(int fd, struct nlconfig_t *config)
341385
*/
342386
do {
343387
int fd;
388+
char *path;
389+
390+
/* Resize the namespace array. */
391+
nses = realloc(nses, ++num * sizeof(struct namespace_t));
344392

345-
/* Resize fds. */
346-
num++;
347-
fds = realloc(fds, num * sizeof(int));
348-
paths = realloc(paths, num * sizeof(char *));
393+
/* Split 'ns:path'. */
394+
path = strstr(ns, ":");
395+
if (!path)
396+
bail("failed to parse %s", ns);
397+
*path++ = '\0';
349398

350-
fd = open(ns, O_RDONLY);
399+
fd = open(path, O_RDONLY);
351400
if (fd < 0)
352401
bail("failed to open %s", ns);
353402

354-
fds[num - 1] = fd;
355-
paths[num - 1] = ns;
403+
nses[num - 1] = (struct namespace_t) {
404+
.fd = fd,
405+
.ns = nsflag(ns),
406+
.path = path,
407+
};
356408
} while ((ns = strtok_r(NULL, ",", &saveptr)) != NULL);
357409

358410
for (i = 0; i < num; i++) {
359-
int fd = fds[i];
360-
char *path = paths[i];
411+
struct namespace_t ns = nses[i];
361412

362-
if (setns(fd, 0) < 0)
363-
bail("failed to setns to %s", path);
413+
/* Actually join the namespaces. */
414+
if (setns(ns.fd, ns.ns) < 0)
415+
bail("failed to setns to %s", ns.path);
364416

365-
close(fd);
417+
close(ns.fd);
366418
}
367419

368-
free(fds);
369-
free(paths);
420+
free(nses);
370421
break;
371422
}
372423
case UIDMAP_ATTR:

0 commit comments

Comments
 (0)