Skip to content

Commit f6195ce

Browse files
committed
libct/nsenter: better read/write errors
Introduce and use CHECK_IO and CHECK_IO_CLEANUP macros so that we can append %m to the error message depending on read/write return value. This prevents the "Success" prefix in errors like: failed to sync with stage-1: next state: Success Signed-off-by: Kir Kolyshkin <[email protected]>
1 parent d546c09 commit f6195ce

File tree

1 file changed

+69
-65
lines changed

1 file changed

+69
-65
lines changed

libcontainer/nsenter/nsexec.c

Lines changed: 69 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,42 @@ int setns(int fd, int nstype)
132132
}
133133
#endif
134134

135+
/*
136+
* Check the result of read(2) or write(2) system calls.
137+
* If the syscall returned -1 (error), call bail() to report errno.
138+
* Otherwise, if the actual bytes transferred differs from expected,
139+
* call bailx() since errno is not meaningful for short reads/writes.
140+
*
141+
* Usage: CHECK_IO(read, syncfd, &s, sizeof(s), "failed to sync with parent");
142+
*/
143+
#define CHECK_IO(op, fd, buf, count, ...) \
144+
do { \
145+
ssize_t __ret = op(fd, buf, count); \
146+
if (__ret < 0) \
147+
bail(__VA_ARGS__); \
148+
if (__ret != (ssize_t)(count)) \
149+
bailx(__VA_ARGS__ ": short " #op); \
150+
} while (0)
151+
152+
/*
153+
* CHECK_IO variant that kills PIDs before bailing.
154+
* Use this when you need to clean up child processes on I/O failure.
155+
*
156+
* Usage: CHECK_IO_CLEANUP(write, syncfd, &s, sizeof(s), stage1_pid, stage2_pid,
157+
* "failed to sync");
158+
*/
159+
#define CHECK_IO_CLEANUP(op, fd, buf, count, pid1, pid2, ...) \
160+
do { \
161+
ssize_t __ret = op(fd, buf, count); \
162+
if (__ret != (ssize_t)(count)) { \
163+
sane_kill(pid1, SIGKILL); \
164+
sane_kill(pid2, SIGKILL); \
165+
if (__ret < 0) \
166+
bail(__VA_ARGS__); \
167+
bailx(__VA_ARGS__ ": short " #op); \
168+
} \
169+
} while (0)
170+
135171
/* XXX: This is ugly. */
136172
static int syncfd = -1;
137173

@@ -334,16 +370,12 @@ static uint8_t readint8(char *buf)
334370

335371
static void nl_parse(int fd, struct nlconfig_t *config)
336372
{
337-
size_t len, size;
373+
size_t size;
338374
struct nlmsghdr hdr;
339375
char *data, *current;
340376

341377
/* Retrieve the netlink header. */
342-
len = read(fd, &hdr, NLMSG_HDRLEN);
343-
if (len < 0)
344-
bail("failed to read netlink header");
345-
if (len != NLMSG_HDRLEN)
346-
bailx("invalid netlink header length %zu", len);
378+
CHECK_IO(read, fd, &hdr, NLMSG_HDRLEN, "failed to read netlink header");
347379

348380
if (hdr.nlmsg_type == NLMSG_ERROR)
349381
bailx("failed to read netlink message");
@@ -357,11 +389,7 @@ static void nl_parse(int fd, struct nlconfig_t *config)
357389
if (!data)
358390
bail("failed to allocate %zu bytes of memory for nl_payload", size);
359391

360-
len = read(fd, data, size);
361-
if (len < 0)
362-
bail("failed to read netlink payload");
363-
if (len != size)
364-
bailx("failed to read netlink payload, %zu != %zu", len, size);
392+
CHECK_IO(read, fd, data, size, "failed to read netlink payload");
365393

366394
/* Parse the netlink payload. */
367395
config->data = data;
@@ -863,8 +891,7 @@ void nsexec(void)
863891
while (!stage1_complete) {
864892
enum sync_t s;
865893

866-
if (read(syncfd, &s, sizeof(s)) != sizeof(s))
867-
bail("failed to sync with stage-1: next state");
894+
CHECK_IO(read, syncfd, &s, sizeof(s), "failed to sync with stage-1: next state");
868895

869896
switch (s) {
870897
case SYNC_USERMAP_PLS:
@@ -888,28 +915,20 @@ void nsexec(void)
888915
update_gidmap(config.gidmappath, stage1_pid, config.gidmap, config.gidmap_len);
889916

890917
s = SYNC_USERMAP_ACK;
891-
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
892-
sane_kill(stage1_pid, SIGKILL);
893-
sane_kill(stage2_pid, SIGKILL);
894-
bail("failed to sync with stage-1: write(SYNC_USERMAP_ACK)");
895-
}
918+
CHECK_IO_CLEANUP(write, syncfd, &s, sizeof(s), stage1_pid, stage2_pid,
919+
"failed to sync with stage-1: write(SYNC_USERMAP_ACK)");
896920
break;
897921
case SYNC_RECVPID_PLS:
898922
write_log(DEBUG, "stage-1 requested pid to be forwarded");
899923

900924
/* Get the stage-2 pid. */
901-
if (read(syncfd, &stage2_pid, sizeof(stage2_pid)) != sizeof(stage2_pid)) {
902-
sane_kill(stage1_pid, SIGKILL);
903-
bail("failed to sync with stage-1: read(stage2_pid)");
904-
}
925+
CHECK_IO_CLEANUP(read, syncfd, &stage2_pid, sizeof(stage2_pid), stage1_pid, -1,
926+
"failed to sync with stage-1: read(stage2_pid)");
905927

906928
/* Send ACK. */
907929
s = SYNC_RECVPID_ACK;
908-
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
909-
sane_kill(stage1_pid, SIGKILL);
910-
sane_kill(stage2_pid, SIGKILL);
911-
bail("failed to sync with stage-1: write(SYNC_RECVPID_ACK)");
912-
}
930+
CHECK_IO_CLEANUP(write, syncfd, &s, sizeof(s), stage1_pid, stage2_pid,
931+
"failed to sync with stage-1: write(SYNC_RECVPID_ACK)");
913932

914933
/*
915934
* Send both the stage-1 and stage-2 pids back to runc.
@@ -933,10 +952,8 @@ void nsexec(void)
933952
write_log(DEBUG, "stage-1 requested timens offsets to be configured");
934953
update_timens_offsets(stage1_pid, config.timensoffset, config.timensoffset_len);
935954
s = SYNC_TIMEOFFSETS_ACK;
936-
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
937-
sane_kill(stage1_pid, SIGKILL);
938-
bail("failed to sync with child: write(SYNC_TIMEOFFSETS_ACK)");
939-
}
955+
CHECK_IO_CLEANUP(write, syncfd, &s, sizeof(s), stage1_pid, -1,
956+
"failed to sync with child: write(SYNC_TIMEOFFSETS_ACK)");
940957
break;
941958
case SYNC_CHILD_FINISH:
942959
write_log(DEBUG, "stage-1 complete");
@@ -960,13 +977,10 @@ void nsexec(void)
960977

961978
write_log(DEBUG, "signalling stage-2 to run");
962979
s = SYNC_GRANDCHILD;
963-
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
964-
sane_kill(stage2_pid, SIGKILL);
965-
bail("failed to sync with child: write(SYNC_GRANDCHILD)");
966-
}
980+
CHECK_IO_CLEANUP(write, syncfd, &s, sizeof(s), stage2_pid, -1,
981+
"failed to sync with child: write(SYNC_GRANDCHILD)");
967982

968-
if (read(syncfd, &s, sizeof(s)) != sizeof(s))
969-
bail("failed to sync with child: next state");
983+
CHECK_IO(read, syncfd, &s, sizeof(s), "failed to sync with child: next state");
970984

971985
switch (s) {
972986
case SYNC_CHILD_FINISH:
@@ -1057,13 +1071,13 @@ void nsexec(void)
10571071
*/
10581072
write_log(DEBUG, "request stage-0 to map user namespace");
10591073
s = SYNC_USERMAP_PLS;
1060-
if (write(syncfd, &s, sizeof(s)) != sizeof(s))
1061-
bail("failed to sync with parent: write(SYNC_USERMAP_PLS)");
1074+
CHECK_IO(write, syncfd, &s, sizeof(s),
1075+
"failed to sync with parent: write(SYNC_USERMAP_PLS)");
10621076

10631077
/* ... wait for mapping ... */
10641078
write_log(DEBUG, "waiting stage-0 to complete the mapping of user namespace");
1065-
if (read(syncfd, &s, sizeof(s)) != sizeof(s))
1066-
bail("failed to sync with parent: read(SYNC_USERMAP_ACK)");
1079+
CHECK_IO(read, syncfd, &s, sizeof(s),
1080+
"failed to sync with parent: read(SYNC_USERMAP_ACK)");
10671081
if (s != SYNC_USERMAP_ACK)
10681082
bailx("failed to sync with parent: SYNC_USERMAP_ACK: got %u", s);
10691083

@@ -1095,11 +1109,11 @@ void nsexec(void)
10951109
write_log(DEBUG, "request stage-0 to write timens offsets");
10961110

10971111
s = SYNC_TIMEOFFSETS_PLS;
1098-
if (write(syncfd, &s, sizeof(s)) != sizeof(s))
1099-
bail("failed to sync with parent: write(SYNC_TIMEOFFSETS_PLS)");
1112+
CHECK_IO(write, syncfd, &s, sizeof(s),
1113+
"failed to sync with parent: write(SYNC_TIMEOFFSETS_PLS)");
11001114

1101-
if (read(syncfd, &s, sizeof(s)) != sizeof(s))
1102-
bail("failed to sync with parent: read(SYNC_TIMEOFFSETS_ACK)");
1115+
CHECK_IO(read, syncfd, &s, sizeof(s),
1116+
"failed to sync with parent: read(SYNC_TIMEOFFSETS_ACK)");
11031117
if (s != SYNC_TIMEOFFSETS_ACK)
11041118
bailx("failed to sync with parent: SYNC_TIMEOFFSETS_ACK: got %u", s);
11051119
}
@@ -1121,31 +1135,23 @@ void nsexec(void)
11211135
/* Send the child to our parent, which knows what it's doing. */
11221136
write_log(DEBUG, "request stage-0 to forward stage-2 pid (%d)", stage2_pid);
11231137
s = SYNC_RECVPID_PLS;
1124-
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
1125-
sane_kill(stage2_pid, SIGKILL);
1126-
bail("failed to sync with parent: write(SYNC_RECVPID_PLS)");
1127-
}
1128-
if (write(syncfd, &stage2_pid, sizeof(stage2_pid)) != sizeof(stage2_pid)) {
1129-
sane_kill(stage2_pid, SIGKILL);
1130-
bail("failed to sync with parent: write(stage2_pid)");
1131-
}
1138+
CHECK_IO_CLEANUP(write, syncfd, &s, sizeof(s), stage2_pid, -1,
1139+
"failed to sync with parent: write(SYNC_RECVPID_PLS)");
1140+
CHECK_IO_CLEANUP(write, syncfd, &stage2_pid, sizeof(stage2_pid), stage2_pid, -1,
1141+
"failed to sync with parent: write(stage2_pid)");
11321142

11331143
/* ... wait for parent to get the pid ... */
1134-
if (read(syncfd, &s, sizeof(s)) != sizeof(s)) {
1135-
sane_kill(stage2_pid, SIGKILL);
1136-
bail("failed to sync with parent: read(SYNC_RECVPID_ACK)");
1137-
}
1144+
CHECK_IO_CLEANUP(read, syncfd, &s, sizeof(s), stage2_pid, -1,
1145+
"failed to sync with parent: read(SYNC_RECVPID_ACK)");
11381146
if (s != SYNC_RECVPID_ACK) {
11391147
sane_kill(stage2_pid, SIGKILL);
11401148
bailx("failed to sync with parent: SYNC_RECVPID_ACK: got %u", s);
11411149
}
11421150

11431151
write_log(DEBUG, "signal completion to stage-0");
11441152
s = SYNC_CHILD_FINISH;
1145-
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) {
1146-
sane_kill(stage2_pid, SIGKILL);
1147-
bail("failed to sync with parent: write(SYNC_CHILD_FINISH)");
1148-
}
1153+
CHECK_IO_CLEANUP(write, syncfd, &s, sizeof(s), stage2_pid, -1,
1154+
"failed to sync with parent: write(SYNC_CHILD_FINISH)");
11491155

11501156
/* Our work is done. [Stage 2: STAGE_INIT] is doing the rest of the work. */
11511157
write_log(DEBUG, "<~ nsexec stage-1");
@@ -1181,8 +1187,7 @@ void nsexec(void)
11811187
prctl(PR_SET_NAME, (unsigned long)"runc:[2:INIT]", 0, 0, 0);
11821188
write_log(DEBUG, "~> nsexec stage-2");
11831189

1184-
if (read(syncfd, &s, sizeof(s)) != sizeof(s))
1185-
bail("failed to sync with parent: read(SYNC_GRANDCHILD)");
1190+
CHECK_IO(read, syncfd, &s, sizeof(s), "failed to sync with parent: read(SYNC_GRANDCHILD)");
11861191
if (s != SYNC_GRANDCHILD)
11871192
bailx("failed to sync with parent: SYNC_GRANDCHILD: got %u", s);
11881193

@@ -1202,8 +1207,7 @@ void nsexec(void)
12021207

12031208
write_log(DEBUG, "signal completion to stage-0");
12041209
s = SYNC_CHILD_FINISH;
1205-
if (write(syncfd, &s, sizeof(s)) != sizeof(s))
1206-
bail("failed to sync with parent: write(SYNC_CHILD_FINISH)");
1210+
CHECK_IO(write, syncfd, &s, sizeof(s), "failed to sync with parent: write(SYNC_CHILD_FINISH)");
12071211

12081212
/* Close sync pipes. */
12091213
if (close(sync_grandchild_pipe[0]) < 0)

0 commit comments

Comments
 (0)