Description:
add option -A <opt> to box. This options allow more argument to be explicitly passed to the program We have to use this because if the argument we wish to pass to the program is option (in -? format), box will intepret it as its option and failed accordingly. be noted that, by the definition of getopt, these options will be put after original argument (check the code for more info)
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r186:c8d646326d0a - - 1 file changed: 17 inserted, 4 deleted

@@ -891,857 +891,870
891 891 }
892 892 else
893 893 r->val = NULL;
894 894 *last_env_rule = r;
895 895 last_env_rule = &r->next;
896 896 r->next = NULL;
897 897 return 1;
898 898 }
899 899
900 900 static int
901 901 match_env_var(char *env_entry, struct env_rule *r)
902 902 {
903 903 if (strncmp(env_entry, r->var, r->var_len))
904 904 return 0;
905 905 return (env_entry[r->var_len] == '=');
906 906 }
907 907
908 908 static void
909 909 apply_env_rule(char **env, int *env_sizep, struct env_rule *r)
910 910 {
911 911 // First remove the variable if already set
912 912 int pos = 0;
913 913 while (pos < *env_sizep && !match_env_var(env[pos], r))
914 914 pos++;
915 915 if (pos < *env_sizep)
916 916 {
917 917 (*env_sizep)--;
918 918 env[pos] = env[*env_sizep];
919 919 env[*env_sizep] = NULL;
920 920 }
921 921
922 922 // What is the new value?
923 923 char *new;
924 924 if (r->val)
925 925 {
926 926 if (!r->val[0])
927 927 return;
928 928 new = xmalloc(r->var_len + 1 + strlen(r->val) + 1);
929 929 sprintf(new, "%s=%s", r->var, r->val);
930 930 }
931 931 else
932 932 {
933 933 pos = 0;
934 934 while (environ[pos] && !match_env_var(environ[pos], r))
935 935 pos++;
936 936 if (!(new = environ[pos]))
937 937 return;
938 938 }
939 939
940 940 // Add it at the end of the array
941 941 env[(*env_sizep)++] = new;
942 942 env[*env_sizep] = NULL;
943 943 }
944 944
945 945 static char **
946 946 setup_environment(void)
947 947 {
948 948 // Link built-in rules with user rules
949 949 for (int i=ARRAY_SIZE(default_env_rules)-1; i >= 0; i--)
950 950 {
951 951 default_env_rules[i].next = first_env_rule;
952 952 first_env_rule = &default_env_rules[i];
953 953 }
954 954
955 955 // Scan the original environment
956 956 char **orig_env = environ;
957 957 int orig_size = 0;
958 958 while (orig_env[orig_size])
959 959 orig_size++;
960 960
961 961 // For each rule, reserve one more slot and calculate length
962 962 int num_rules = 0;
963 963 for (struct env_rule *r = first_env_rule; r; r=r->next)
964 964 {
965 965 num_rules++;
966 966 r->var_len = strlen(r->var);
967 967 }
968 968
969 969 // Create a new environment
970 970 char **env = xmalloc((orig_size + num_rules + 1) * sizeof(char *));
971 971 int size;
972 972 if (pass_environ)
973 973 {
974 974 memcpy(env, environ, orig_size * sizeof(char *));
975 975 size = orig_size;
976 976 }
977 977 else
978 978 size = 0;
979 979 env[size] = NULL;
980 980
981 981 // Apply the rules one by one
982 982 for (struct env_rule *r = first_env_rule; r; r=r->next)
983 983 apply_env_rule(env, &size, r);
984 984
985 985 // Return the new env and pass some gossip
986 986 if (verbose > 1)
987 987 {
988 988 fprintf(stderr, "Passing environment:\n");
989 989 for (int i=0; env[i]; i++)
990 990 fprintf(stderr, "\t%s\n", env[i]);
991 991 }
992 992 return env;
993 993 }
994 994
995 995 /*** Low-level parsing of syscalls ***/
996 996
997 997 #ifdef CONFIG_BOX_KERNEL_AMD64
998 998 typedef uint64_t arg_t;
999 999 #else
1000 1000 typedef uint32_t arg_t;
1001 1001 #endif
1002 1002
1003 1003 struct syscall_args {
1004 1004 arg_t sys;
1005 1005 arg_t arg1, arg2, arg3;
1006 1006 arg_t result;
1007 1007 struct user user;
1008 1008 };
1009 1009
1010 1010 static int user_mem_fd;
1011 1011
1012 1012 static int read_user_mem(arg_t addr, char *buf, int len)
1013 1013 {
1014 1014 if (!user_mem_fd)
1015 1015 {
1016 1016 char memname[64];
1017 1017 sprintf(memname, "/proc/%d/mem", (int) box_pid);
1018 1018 user_mem_fd = open(memname, O_RDONLY);
1019 1019 if (user_mem_fd < 0)
1020 1020 die("open(%s): %m", memname);
1021 1021 }
1022 1022 if (lseek64(user_mem_fd, addr, SEEK_SET) < 0)
1023 1023 die("lseek64(mem): %m");
1024 1024 return read(user_mem_fd, buf, len);
1025 1025 }
1026 1026
1027 1027 static void close_user_mem(void)
1028 1028 {
1029 1029 if (user_mem_fd)
1030 1030 {
1031 1031 close(user_mem_fd);
1032 1032 user_mem_fd = 0;
1033 1033 }
1034 1034 }
1035 1035
1036 1036 #ifdef CONFIG_BOX_KERNEL_AMD64
1037 1037
1038 1038 static void
1039 1039 get_syscall_args(struct syscall_args *a, int is_exit)
1040 1040 {
1041 1041 if (ptrace(PTRACE_GETREGS, box_pid, NULL, &a->user) < 0)
1042 1042 die("ptrace(PTRACE_GETREGS): %m");
1043 1043 a->sys = a->user.regs.orig_rax;
1044 1044 a->result = a->user.regs.rax;
1045 1045
1046 1046 /*
1047 1047 * CAVEAT: We have to check carefully that this is a real 64-bit syscall.
1048 1048 * We test whether the process runs in 64-bit mode, but surprisingly this
1049 1049 * is not enough: a 64-bit process can still issue the INT 0x80 instruction
1050 1050 * which performs a 32-bit syscall. Currently, the only known way how to
1051 1051 * detect this situation is to inspect the instruction code (the kernel
1052 1052 * keeps a syscall type flag internally, but it is not accessible from
1053 1053 * user space). Hopefully, there is no instruction whose suffix is the
1054 1054 * code of the SYSCALL instruction. Sometimes, one would wish the
1055 1055 * instruction codes to be unique even when read backwards :)
1056 1056 */
1057 1057
1058 1058 if (is_exit)
1059 1059 return;
1060 1060
1061 1061 int sys_type;
1062 1062 uint16_t instr;
1063 1063
1064 1064 switch (a->user.regs.cs)
1065 1065 {
1066 1066 case 0x23:
1067 1067 // 32-bit CPU mode => only 32-bit syscalls can be issued
1068 1068 sys_type = 32;
1069 1069 break;
1070 1070 case 0x33:
1071 1071 // 64-bit CPU mode
1072 1072 if (read_user_mem(a->user.regs.rip-2, (char *) &instr, 2) != 2)
1073 1073 err("FO: Cannot read syscall instruction");
1074 1074 switch (instr)
1075 1075 {
1076 1076 case 0x050f:
1077 1077 break;
1078 1078 case 0x80cd:
1079 1079 err("FO: Forbidden 32-bit syscall in 64-bit mode");
1080 1080 default:
1081 1081 err("XX: Unknown syscall instruction %04x", instr);
1082 1082 }
1083 1083 sys_type = 64;
1084 1084 break;
1085 1085 default:
1086 1086 err("XX: Unknown code segment %04jx", (intmax_t) a->user.regs.cs);
1087 1087 }
1088 1088
1089 1089 #ifdef CONFIG_BOX_USER_AMD64
1090 1090 if (sys_type != 64)
1091 1091 err("FO: Forbidden %d-bit mode syscall", sys_type);
1092 1092 #else
1093 1093 if (sys_type != (exec_seen ? 32 : 64))
1094 1094 err("FO: Forbidden %d-bit mode syscall", sys_type);
1095 1095 #endif
1096 1096
1097 1097 if (sys_type == 32)
1098 1098 {
1099 1099 a->arg1 = a->user.regs.rbx;
1100 1100 a->arg2 = a->user.regs.rcx;
1101 1101 a->arg3 = a->user.regs.rdx;
1102 1102 }
1103 1103 else
1104 1104 {
1105 1105 a->arg1 = a->user.regs.rdi;
1106 1106 a->arg2 = a->user.regs.rsi;
1107 1107 a->arg3 = a->user.regs.rdx;
1108 1108 }
1109 1109 }
1110 1110
1111 1111 static void
1112 1112 set_syscall_nr(struct syscall_args *a, arg_t sys)
1113 1113 {
1114 1114 a->sys = sys;
1115 1115 a->user.regs.orig_rax = sys;
1116 1116 if (ptrace(PTRACE_SETREGS, box_pid, NULL, &a->user) < 0)
1117 1117 die("ptrace(PTRACE_SETREGS): %m");
1118 1118 }
1119 1119
1120 1120 static void
1121 1121 sanity_check(void)
1122 1122 {
1123 1123 }
1124 1124
1125 1125 #else
1126 1126
1127 1127 static void
1128 1128 get_syscall_args(struct syscall_args *a, int is_exit UNUSED)
1129 1129 {
1130 1130 if (ptrace(PTRACE_GETREGS, box_pid, NULL, &a->user) < 0)
1131 1131 die("ptrace(PTRACE_GETREGS): %m");
1132 1132 a->sys = a->user.regs.orig_eax;
1133 1133 a->arg1 = a->user.regs.ebx;
1134 1134 a->arg2 = a->user.regs.ecx;
1135 1135 a->arg3 = a->user.regs.edx;
1136 1136 a->result = a->user.regs.eax;
1137 1137 }
1138 1138
1139 1139 static void
1140 1140 set_syscall_nr(struct syscall_args *a, arg_t sys)
1141 1141 {
1142 1142 a->sys = sys;
1143 1143 a->user.regs.orig_eax = sys;
1144 1144 if (ptrace(PTRACE_SETREGS, box_pid, NULL, &a->user) < 0)
1145 1145 die("ptrace(PTRACE_SETREGS): %m");
1146 1146 }
1147 1147
1148 1148 static void
1149 1149 sanity_check(void)
1150 1150 {
1151 1151 #if !defined(CONFIG_BOX_ALLOW_INSECURE)
1152 1152 struct utsname uts;
1153 1153 if (uname(&uts) < 0)
1154 1154 die("uname() failed: %m");
1155 1155
1156 1156 if (!strcmp(uts.machine, "x86_64"))
1157 1157 die("Running 32-bit sandbox on 64-bit kernels is inherently unsafe. Please get a 64-bit version.");
1158 1158 #endif
1159 1159 }
1160 1160
1161 1161 #endif
1162 1162
1163 1163 /*** Syscall checks ***/
1164 1164
1165 1165 static void
1166 1166 valid_filename(arg_t addr)
1167 1167 {
1168 1168 char namebuf[4096], *p, *end;
1169 1169
1170 1170 if (!file_access)
1171 1171 err("FA: File access forbidden");
1172 1172 if (file_access >= 9)
1173 1173 return;
1174 1174
1175 1175 p = end = namebuf;
1176 1176 do
1177 1177 {
1178 1178 if (p >= end)
1179 1179 {
1180 1180 int remains = PAGE_SIZE - (addr & (PAGE_SIZE-1));
1181 1181 int l = namebuf + sizeof(namebuf) - end;
1182 1182 if (l > remains)
1183 1183 l = remains;
1184 1184 if (!l)
1185 1185 err("FA: Access to file with name too long");
1186 1186 remains = read_user_mem(addr, end, l);
1187 1187 if (remains < 0)
1188 1188 die("read(mem): %m");
1189 1189 if (!remains)
1190 1190 err("FA: Access to file with name out of memory");
1191 1191 end += remains;
1192 1192 addr += remains;
1193 1193 }
1194 1194 }
1195 1195 while (*p++);
1196 1196
1197 1197 msg("[%s] ", namebuf);
1198 1198 if (file_access >= 3)
1199 1199 return;
1200 1200
1201 1201 // Everything in current directory is permitted
1202 1202 if (!strchr(namebuf, '/') && strcmp(namebuf, ".."))
1203 1203 return;
1204 1204
1205 1205 // ".." anywhere in the path is forbidden
1206 1206 enum action act = A_DEFAULT;
1207 1207 if (strstr(namebuf, ".."))
1208 1208 act = A_NO;
1209 1209
1210 1210 // Scan user rules
1211 1211 for (struct path_rule *r = user_path_rules; r && !act; r=r->next)
1212 1212 act = match_path_rule(r, namebuf);
1213 1213
1214 1214 // Scan built-in rules
1215 1215 if (file_access >= 2)
1216 1216 for (int i=0; i<ARRAY_SIZE(default_path_rules) && !act; i++)
1217 1217 act = match_path_rule(&default_path_rules[i], namebuf);
1218 1218
1219 1219 if (act != A_YES)
1220 1220 err("FA: Forbidden access to file `%s'", namebuf);
1221 1221 }
1222 1222
1223 1223 // Check syscall. If invalid, return -1, otherwise return the action mask.
1224 1224 static int
1225 1225 valid_syscall(struct syscall_args *a)
1226 1226 {
1227 1227 unsigned int sys = a->sys;
1228 1228 unsigned int act = (sys < NUM_ACTIONS) ? syscall_action[sys] : A_DEFAULT;
1229 1229
1230 1230 if (act & A_LIBERAL)
1231 1231 {
1232 1232 if (filter_syscalls != 1)
1233 1233 act = A_DEFAULT;
1234 1234 }
1235 1235
1236 1236 switch (act & A_ACTION_MASK)
1237 1237 {
1238 1238 case A_YES:
1239 1239 return act;
1240 1240 case A_NO:
1241 1241 return -1;
1242 1242 case A_FILENAME:
1243 1243 valid_filename(a->arg1);
1244 1244 return act;
1245 1245 default: ;
1246 1246 }
1247 1247
1248 1248 switch (sys)
1249 1249 {
1250 1250 case __NR_kill:
1251 1251 if (a->arg1 == (arg_t) box_pid)
1252 1252 {
1253 1253 meta_printf("exitsig:%d\n", (int) a->arg2);
1254 1254 err("SG: Committed suicide by signal %d", (int) a->arg2);
1255 1255 }
1256 1256 return -1;
1257 1257 case __NR_tgkill:
1258 1258 if (a->arg1 == (arg_t) box_pid && a->arg2 == (arg_t) box_pid)
1259 1259 {
1260 1260 meta_printf("exitsig:%d\n", (int) a->arg3);
1261 1261 err("SG: Committed suicide by signal %d", (int) a->arg3);
1262 1262 }
1263 1263 return -1;
1264 1264 default:
1265 1265 return -1;
1266 1266 }
1267 1267 }
1268 1268
1269 1269 static void
1270 1270 signal_alarm(int unused UNUSED)
1271 1271 {
1272 1272 /* Time limit checks are synchronous, so we only schedule them there. */
1273 1273 timer_tick = 1;
1274 1274 alarm(1);
1275 1275 }
1276 1276
1277 1277 static void
1278 1278 signal_int(int unused UNUSED)
1279 1279 {
1280 1280 /* Interrupts are fatal, so no synchronization requirements. */
1281 1281 meta_printf("exitsig:%d\n", SIGINT);
1282 1282 err("SG: Interrupted");
1283 1283 }
1284 1284
1285 1285 #define PROC_BUF_SIZE 4096
1286 1286 static void
1287 1287 read_proc_file(char *buf, char *name, int *fdp)
1288 1288 {
1289 1289 int c;
1290 1290
1291 1291 if (!*fdp)
1292 1292 {
1293 1293 sprintf(buf, "/proc/%d/%s", (int) box_pid, name);
1294 1294 *fdp = open(buf, O_RDONLY);
1295 1295 if (*fdp < 0)
1296 1296 die("open(%s): %m", buf);
1297 1297 }
1298 1298 lseek(*fdp, 0, SEEK_SET);
1299 1299 if ((c = read(*fdp, buf, PROC_BUF_SIZE-1)) < 0)
1300 1300 die("read on /proc/$pid/%s: %m", name);
1301 1301 if (c >= PROC_BUF_SIZE-1)
1302 1302 die("/proc/$pid/%s too long", name);
1303 1303 buf[c] = 0;
1304 1304 }
1305 1305
1306 1306 static void
1307 1307 check_timeout(void)
1308 1308 {
1309 1309 if (wall_timeout)
1310 1310 {
1311 1311 struct timeval now, wall;
1312 1312 int wall_ms;
1313 1313 gettimeofday(&now, NULL);
1314 1314 timersub(&now, &start_time, &wall);
1315 1315 wall_ms = wall.tv_sec*1000 + wall.tv_usec/1000;
1316 1316 if (wall_ms > wall_timeout)
1317 1317 err("TO: Time limit exceeded (wall clock)");
1318 1318 if (verbose > 1)
1319 1319 fprintf(stderr, "[wall time check: %d msec]\n", wall_ms);
1320 1320 }
1321 1321 if (timeout)
1322 1322 {
1323 1323 char buf[PROC_BUF_SIZE], *x;
1324 1324 int utime, stime, ms;
1325 1325 static int proc_stat_fd;
1326 1326 read_proc_file(buf, "stat", &proc_stat_fd);
1327 1327 x = buf;
1328 1328 while (*x && *x != ' ')
1329 1329 x++;
1330 1330 while (*x == ' ')
1331 1331 x++;
1332 1332 if (*x++ != '(')
1333 1333 die("proc stat syntax error 1");
1334 1334 while (*x && (*x != ')' || x[1] != ' '))
1335 1335 x++;
1336 1336 while (*x == ')' || *x == ' ')
1337 1337 x++;
1338 1338 if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
1339 1339 die("proc stat syntax error 2");
1340 1340 ms = (utime + stime) * 1000 / ticks_per_sec;
1341 1341 if (verbose > 1)
1342 1342 fprintf(stderr, "[time check: %d msec]\n", ms);
1343 1343 if (ms > timeout && ms > extra_timeout)
1344 1344 err("TO: Time limit exceeded");
1345 1345 }
1346 1346 }
1347 1347
1348 1348 static void
1349 1349 sample_mem_peak(void)
1350 1350 {
1351 1351 /*
1352 1352 * We want to find out the peak memory usage of the process, which is
1353 1353 * maintained by the kernel, but unforunately it gets lost when the
1354 1354 * process exits (it is not reported in struct rusage). Therefore we
1355 1355 * have to sample it whenever we suspect that the process is about
1356 1356 * to exit.
1357 1357 */
1358 1358 char buf[PROC_BUF_SIZE], *x;
1359 1359 static int proc_status_fd;
1360 1360 read_proc_file(buf, "status", &proc_status_fd);
1361 1361
1362 1362 x = buf;
1363 1363 while (*x)
1364 1364 {
1365 1365 char *key = x;
1366 1366 while (*x && *x != ':' && *x != '\n')
1367 1367 x++;
1368 1368 if (!*x || *x == '\n')
1369 1369 break;
1370 1370 *x++ = 0;
1371 1371 while (*x == ' ' || *x == '\t')
1372 1372 x++;
1373 1373
1374 1374 char *val = x;
1375 1375 while (*x && *x != '\n')
1376 1376 x++;
1377 1377 if (!*x)
1378 1378 break;
1379 1379 *x++ = 0;
1380 1380
1381 1381 if (!strcmp(key, "VmPeak"))
1382 1382 {
1383 1383 int peak = atoi(val);
1384 1384 if (peak > mem_peak_kb)
1385 1385 mem_peak_kb = peak;
1386 1386 }
1387 1387 }
1388 1388
1389 1389 if (verbose > 1)
1390 1390 msg("[mem-peak: %u KB]\n", mem_peak_kb);
1391 1391 }
1392 1392
1393 1393 static void
1394 1394 boxkeeper(void)
1395 1395 {
1396 1396 int syscall_count = (filter_syscalls ? 0 : 1);
1397 1397 struct sigaction sa;
1398 1398
1399 1399 is_ptraced = 1;
1400 1400
1401 1401 bzero(&sa, sizeof(sa));
1402 1402 sa.sa_handler = signal_int;
1403 1403 sigaction(SIGINT, &sa, NULL);
1404 1404
1405 1405 gettimeofday(&start_time, NULL);
1406 1406 ticks_per_sec = sysconf(_SC_CLK_TCK);
1407 1407 if (ticks_per_sec <= 0)
1408 1408 die("Invalid ticks_per_sec!");
1409 1409
1410 1410 if (timeout || wall_timeout)
1411 1411 {
1412 1412 sa.sa_handler = signal_alarm;
1413 1413 sigaction(SIGALRM, &sa, NULL);
1414 1414 alarm(1);
1415 1415 }
1416 1416
1417 1417 for(;;)
1418 1418 {
1419 1419 struct rusage rus;
1420 1420 int stat;
1421 1421 pid_t p;
1422 1422 if (timer_tick)
1423 1423 {
1424 1424 check_timeout();
1425 1425 timer_tick = 0;
1426 1426 }
1427 1427 p = wait4(box_pid, &stat, WUNTRACED, &rus);
1428 1428 if (p < 0)
1429 1429 {
1430 1430 if (errno == EINTR)
1431 1431 continue;
1432 1432 die("wait4: %m");
1433 1433 }
1434 1434 if (p != box_pid)
1435 1435 die("wait4: unknown pid %d exited!", p);
1436 1436 if (WIFEXITED(stat))
1437 1437 {
1438 1438 box_pid = 0;
1439 1439 final_stats(&rus);
1440 1440 if (WEXITSTATUS(stat))
1441 1441 {
1442 1442 if (syscall_count)
1443 1443 {
1444 1444 meta_printf("exitcode:%d\n", WEXITSTATUS(stat));
1445 1445 err("RE: Exited with error status %d", WEXITSTATUS(stat));
1446 1446 }
1447 1447 else
1448 1448 {
1449 1449 // Internal error happened inside the child process and it has been already reported.
1450 1450 box_exit(2);
1451 1451 }
1452 1452 }
1453 1453 if (timeout && total_ms > timeout)
1454 1454 err("TO: Time limit exceeded");
1455 1455 if (wall_timeout && wall_ms > wall_timeout)
1456 1456 err("TO: Time limit exceeded (wall clock)");
1457 1457 flush_line();
1458 1458 fprintf(stderr,"OK\n");
1459 1459 box_exit(0);
1460 1460 }
1461 1461 if (WIFSIGNALED(stat))
1462 1462 {
1463 1463 box_pid = 0;
1464 1464 meta_printf("exitsig:%d\n", WTERMSIG(stat));
1465 1465 final_stats(&rus);
1466 1466 err("SG: Caught fatal signal %d%s", WTERMSIG(stat), (syscall_count ? "" : " during startup"));
1467 1467 }
1468 1468 if (WIFSTOPPED(stat))
1469 1469 {
1470 1470 int sig = WSTOPSIG(stat);
1471 1471 if (sig == SIGTRAP)
1472 1472 {
1473 1473 if (verbose > 2)
1474 1474 msg("[ptrace status %08x] ", stat);
1475 1475 static int stop_count;
1476 1476 if (!stop_count++) /* Traceme request */
1477 1477 msg(">> Traceme request caught\n");
1478 1478 else
1479 1479 err("SG: Breakpoint");
1480 1480 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1481 1481 }
1482 1482 else if (sig == (SIGTRAP | 0x80))
1483 1483 {
1484 1484 if (verbose > 2)
1485 1485 msg("[ptrace status %08x] ", stat);
1486 1486 struct syscall_args a;
1487 1487 static unsigned int sys_tick, last_act;
1488 1488 static arg_t last_sys;
1489 1489 if (++sys_tick & 1) /* Syscall entry */
1490 1490 {
1491 1491 char namebuf[32];
1492 1492 int act;
1493 1493
1494 1494 get_syscall_args(&a, 0);
1495 1495 arg_t sys = a.sys;
1496 1496 msg(">> Syscall %-12s (%08jx,%08jx,%08jx) ", syscall_name(sys, namebuf), (intmax_t) a.arg1, (intmax_t) a.arg2, (intmax_t) a.arg3);
1497 1497 if (!exec_seen)
1498 1498 {
1499 1499 msg("[master] ");
1500 1500 if (sys == NATIVE_NR_execve)
1501 1501 {
1502 1502 exec_seen = 1;
1503 1503 close_user_mem();
1504 1504 }
1505 1505 }
1506 1506 else if ((act = valid_syscall(&a)) >= 0)
1507 1507 {
1508 1508 last_act = act;
1509 1509 syscall_count++;
1510 1510 if (act & A_SAMPLE_MEM)
1511 1511 sample_mem_peak();
1512 1512 }
1513 1513 else
1514 1514 {
1515 1515 /*
1516 1516 * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
1517 1517 * so we have to change it to something harmless (e.g., an undefined
1518 1518 * syscall) and make the program continue.
1519 1519 */
1520 1520 set_syscall_nr(&a, ~(arg_t)0);
1521 1521 err("FO: Forbidden syscall %s", syscall_name(sys, namebuf));
1522 1522 }
1523 1523 last_sys = sys;
1524 1524 }
1525 1525 else /* Syscall return */
1526 1526 {
1527 1527 get_syscall_args(&a, 1);
1528 1528 if (a.sys == ~(arg_t)0)
1529 1529 {
1530 1530 /* Some syscalls (sigreturn et al.) do not return a value */
1531 1531 if (!(last_act & A_NO_RETVAL))
1532 1532 err("XX: Syscall does not return, but it should");
1533 1533 }
1534 1534 else
1535 1535 {
1536 1536 if (a.sys != last_sys)
1537 1537 err("XX: Mismatched syscall entry/exit");
1538 1538 }
1539 1539 if (last_act & A_NO_RETVAL)
1540 1540 msg("= ?\n");
1541 1541 else
1542 1542 msg("= %jd\n", (intmax_t) a.result);
1543 1543 }
1544 1544 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1545 1545 }
1546 1546 else if (sig == SIGSTOP)
1547 1547 {
1548 1548 msg(">> SIGSTOP\n");
1549 1549 if (ptrace(PTRACE_SETOPTIONS, box_pid, NULL, (void *) PTRACE_O_TRACESYSGOOD) < 0)
1550 1550 die("ptrace(PTRACE_SETOPTIONS): %m");
1551 1551 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1552 1552 }
1553 1553 else if (sig != SIGXCPU && sig != SIGXFSZ)
1554 1554 {
1555 1555 msg(">> Signal %d\n", sig);
1556 1556 sample_mem_peak(); /* Signal might be fatal, so update mem-peak */
1557 1557 ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
1558 1558 }
1559 1559 else
1560 1560 {
1561 1561 meta_printf("exitsig:%d", sig);
1562 1562 err("SG: Received signal %d", sig);
1563 1563 }
1564 1564 }
1565 1565 else
1566 1566 die("wait4: unknown status %x, giving up!", stat);
1567 1567 }
1568 1568 }
1569 1569
1570 1570 static void
1571 1571 box_inside(int argc, char **argv)
1572 1572 {
1573 1573 struct rlimit rl;
1574 1574 char *args[argc+1];
1575 1575
1576 1576 memcpy(args, argv, argc * sizeof(char *));
1577 1577 args[argc] = NULL;
1578 1578 if (set_cwd && chdir(set_cwd))
1579 1579 die("chdir: %m");
1580 1580 if (redir_stdin)
1581 1581 {
1582 1582 close(0);
1583 1583 if (open(redir_stdin, O_RDONLY) != 0)
1584 1584 die("open(\"%s\"): %m", redir_stdin);
1585 1585 }
1586 1586 if (redir_stdout)
1587 1587 {
1588 1588 close(1);
1589 1589 if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
1590 1590 die("open(\"%s\"): %m", redir_stdout);
1591 1591 }
1592 1592 if (redir_stderr)
1593 1593 {
1594 1594 close(2);
1595 1595 if (open(redir_stderr, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 2)
1596 1596 die("open(\"%s\"): %m", redir_stderr);
1597 1597 }
1598 1598 else
1599 1599 dup2(1, 2);
1600 1600 setpgrp();
1601 1601
1602 1602 if (memory_limit)
1603 1603 {
1604 1604 rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
1605 1605 if (setrlimit(RLIMIT_AS, &rl) < 0)
1606 1606 die("setrlimit(RLIMIT_AS): %m");
1607 1607 }
1608 1608
1609 1609 rl.rlim_cur = rl.rlim_max = (stack_limit ? (rlim_t)stack_limit * 1024 : RLIM_INFINITY);
1610 1610 if (setrlimit(RLIMIT_STACK, &rl) < 0)
1611 1611 die("setrlimit(RLIMIT_STACK): %m");
1612 1612
1613 1613 rl.rlim_cur = rl.rlim_max = 64;
1614 1614 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
1615 1615 die("setrlimit(RLIMIT_NOFILE): %m");
1616 1616
1617 1617 char **env = setup_environment();
1618 1618 if (filter_syscalls)
1619 1619 {
1620 1620 if (ptrace(PTRACE_TRACEME) < 0)
1621 1621 die("ptrace(PTRACE_TRACEME): %m");
1622 1622 /* Trick: Make sure that we are stopped until the boxkeeper wakes up. */
1623 1623 raise(SIGSTOP);
1624 1624 }
1625 1625 execve(args[0], args, env);
1626 1626 die("execve(\"%s\"): %m", args[0]);
1627 1627 }
1628 1628
1629 1629 static void
1630 1630 usage(void)
1631 1631 {
1632 1632 fprintf(stderr, "Invalid arguments!\n");
1633 1633 printf("\
1634 1634 Usage: box [<options>] -- <command> <arguments>\n\
1635 1635 \n\
1636 1636 Options:\n\
1637 1637 -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
1638 1638 -c <dir>\tChange directory to <dir> first\n\
1639 1639 -e\t\tInherit full environment of the parent process\n\
1640 1640 -E <var>\tInherit the environment variable <var> from the parent process\n\
1641 1641 -E <var>=<val>\tSet the environment variable <var> to <val>; unset it if <var> is empty\n\
1642 1642 -f\t\tFilter system calls (-ff=very restricted)\n\
1643 1643 -i <file>\tRedirect stdin from <file>\n\
1644 1644 -k <size>\tLimit stack size to <size> KB (default: 0=unlimited)\n\
1645 1645 -m <size>\tLimit address space to <size> KB\n\
1646 1646 -M <file>\tOutput process information to <file> (name:value)\n\
1647 1647 -o <file>\tRedirect stdout to <file>\n\
1648 1648 -p <path>\tPermit access to the specified path (or subtree if it ends with a `/')\n\
1649 1649 -p <path>=<act>\tDefine action for the specified path (<act>=yes/no)\n\
1650 1650 -r <file>\tRedirect stderr to <file>\n\
1651 1651 -s <sys>\tPermit the specified syscall (be careful)\n\
1652 1652 -s <sys>=<act>\tDefine action for the specified syscall (<act>=yes/no/file)\n\
1653 1653 -t <time>\tSet run time limit (seconds, fractions allowed)\n\
1654 1654 -T\t\tAllow syscalls for measuring run time\n\
1655 1655 -v\t\tBe verbose (use multiple times for even more verbosity)\n\
1656 1656 -w <time>\tSet wall clock time limit (seconds, fractions allowed)\n\
1657 1657 -x <time>\tSet extra timeout, before which a timing-out program is not yet killed,\n\
1658 1658 \t\tso that its real execution time is reported (seconds, fractions allowed)\n\
1659 + -A <opt>\tPass <opt> as additional argument to the <command>\n\
1660 + \t\tBe noted that this option will be appended after <arguments> respectively\n\
1659 1661 ");
1660 1662 exit(2);
1661 1663 }
1662 1664
1663 1665 int
1664 1666 main(int argc, char **argv)
1665 1667 {
1666 1668 int c;
1667 1669 uid_t uid;
1670 + char **prog_argv = xmalloc(sizeof(char*) * argc);
1671 + int prog_argc = 0;
1668 1672
1669 - while ((c = getopt(argc, argv, "a:c:eE:fi:k:m:M:o:p:r:s:t:Tvw:x:")) >= 0)
1673 + while ((c = getopt(argc, argv, "a:c:eE:fi:k:m:M:o:p:r:s:t:Tvw:x:A:")) >= 0)
1670 1674 switch (c)
1671 1675 {
1672 1676 case 'a':
1673 1677 file_access = atol(optarg);
1674 1678 break;
1675 1679 case 'c':
1676 1680 set_cwd = optarg;
1677 1681 break;
1678 1682 case 'e':
1679 1683 pass_environ = 1;
1680 1684 break;
1681 1685 case 'E':
1682 1686 if (!set_env_action(optarg))
1683 1687 usage();
1684 1688 break;
1685 1689 case 'f':
1686 1690 filter_syscalls++;
1687 1691 break;
1688 1692 case 'k':
1689 1693 stack_limit = atol(optarg);
1690 1694 break;
1691 1695 case 'i':
1692 1696 redir_stdin = optarg;
1693 1697 break;
1694 1698 case 'm':
1695 1699 memory_limit = atol(optarg);
1696 1700 break;
1697 1701 case 'M':
1698 1702 meta_open(optarg);
1699 1703 break;
1700 1704 case 'o':
1701 1705 redir_stdout = optarg;
1702 1706 break;
1703 1707 case 'p':
1704 1708 if (!set_path_action(optarg))
1705 1709 usage();
1706 1710 break;
1707 1711 case 'r':
1708 1712 redir_stderr = optarg;
1709 1713 break;
1710 1714 case 's':
1711 1715 if (!set_syscall_action(optarg))
1712 1716 usage();
1713 1717 break;
1714 1718 case 't':
1715 1719 timeout = 1000*atof(optarg);
1716 1720 break;
1717 1721 case 'T':
1718 1722 syscall_action[__NR_times] = A_YES;
1719 1723 break;
1720 1724 case 'v':
1721 1725 verbose++;
1722 1726 break;
1723 1727 case 'w':
1724 1728 wall_timeout = 1000*atof(optarg);
1725 1729 break;
1726 1730 case 'x':
1727 1731 extra_timeout = 1000*atof(optarg);
1732 + case 'A':
1733 + prog_argv[prog_argc++] = strdup(optarg);
1734 + break;
1728 1735 break;
1729 1736 default:
1730 1737 usage();
1731 1738 }
1732 1739 if (optind >= argc)
1733 1740 usage();
1734 1741
1735 1742 sanity_check();
1736 1743 uid = geteuid();
1737 1744 if (setreuid(uid, uid) < 0)
1738 1745 die("setreuid: %m");
1739 1746 box_pid = fork();
1740 1747 if (box_pid < 0)
1741 1748 die("fork: %m");
1742 - if (!box_pid)
1743 - box_inside(argc-optind, argv+optind);
1744 - else
1749 + if (!box_pid) {
1750 + int real_argc = prog_argc + argc - optind;
1751 + char **real_argv = xmalloc(sizeof(char*) * (real_argc));
1752 + for (int i = 0;i < argc-optind;i++)
1753 + real_argv[i] = strdup(argv[i+optind]);
1754 + for (int i = 0;i < prog_argc;i++)
1755 + real_argv[argc - optind + i] = strdup(prog_argv[i]);
1756 + box_inside(real_argc, real_argv);
1757 + } else
1745 1758 boxkeeper();
1746 1759 die("Internal error: fell over edge of the world");
1747 1760 }
You need to be logged in to leave comments. Login now