|
| 1 | +// A file this machine cannot start is "not a program" (kal_err_not_program), |
| 2 | +// whichever of this kernel's values says so (0.15.1): ENOEXEC for a file that |
| 3 | +// is no image, EBADARCH for an image built for a processor this machine does |
| 4 | +// not have. Before, only ENOEXEC was translated, and an image of another |
| 5 | +// processor --- the case a runner asks about --- arrived as an input/output |
| 6 | +// error. |
| 7 | +// |
| 8 | +// The foreign image is a Mach-O header naming a processor that does not exist; |
| 9 | +// the kernel refuses it by its header before reading anything else. |
| 10 | +#include <cstdio> |
| 11 | +#include <cstring> |
| 12 | +#include <fcntl.h> |
| 13 | +#include <unistd.h> |
| 14 | +import openkal.types; |
| 15 | +import openkal.fs; |
| 16 | +import openkal.process; |
| 17 | + |
| 18 | +namespace { |
| 19 | +int failures = 0; |
| 20 | +void check(bool held, const char* what) { |
| 21 | + if (!held) { std::printf("FAIL: %s\n", what); ++failures; } |
| 22 | +} |
| 23 | + |
| 24 | +kal_dir root() { |
| 25 | + for (kal_uintptr i = 0; i < kal_fs_preopen_count(); ++i) { |
| 26 | + kal_dir d{}; char n[8]; kal_uintptr l = 0; |
| 27 | + if (kal_fs_preopen(i, &d, n, sizeof n, &l) == kal_ok && l == 1 && n[0] == '/') return d; |
| 28 | + } |
| 29 | + return kal_dir{}; |
| 30 | +} |
| 31 | + |
| 32 | +// The refusal a start of `path' (absolute) meets. |
| 33 | +int start(const char* path) { |
| 34 | + const kal_dir r = root(); |
| 35 | + const char* argv[] = { path }; |
| 36 | + const kal_uintptr lens[] = { std::strlen(path) }; |
| 37 | + const kal_spawn how{ r, r, nullptr, nullptr, 0, 0 }; |
| 38 | + kal_process p{}; |
| 39 | + const int e = kal_process_spawn(&how, path + 1, std::strlen(path + 1), argv, lens, 1, |
| 40 | + nullptr, nullptr, 0, nullptr, &p); |
| 41 | + if (e == kal_ok) { |
| 42 | + int status = 0, terminated = 0; |
| 43 | + kal_process_wait(p, &status, &terminated); |
| 44 | + } |
| 45 | + return e; |
| 46 | +} |
| 47 | + |
| 48 | +// A file of `bytes', executable, at a fresh name under /tmp. |
| 49 | +bool place(char* path, const unsigned char* bytes, std::size_t n) { |
| 50 | + const int fd = ::mkstemp(path); |
| 51 | + if (fd < 0) return false; |
| 52 | + const bool written = ::write(fd, bytes, n) == static_cast<ssize_t>(n); |
| 53 | + ::fchmod(fd, 0755); |
| 54 | + ::close(fd); |
| 55 | + return written; |
| 56 | +} |
| 57 | +} // namespace |
| 58 | + |
| 59 | +int main() { |
| 60 | + // mach_header_64: magic, a processor type no machine has, a subtype, MH_EXECUTE. |
| 61 | + const unsigned char foreign[32] = { |
| 62 | + 0xcf, 0xfa, 0xed, 0xfe, 0x45, 0x23, 0x01, 0x00, |
| 63 | + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, |
| 64 | + }; |
| 65 | + char image[] = "/tmp/okm-foreign-XXXXXX"; |
| 66 | + check(place(image, foreign, sizeof foreign), "a foreign image is written"); |
| 67 | + check(start(image) == kal_err_not_program, "an image of a processor this machine lacks is not a program"); |
| 68 | + ::unlink(image); |
| 69 | + |
| 70 | + const unsigned char text[] = { 'n', 'o', 't', ' ', 'a', 'n', ' ', 'i', 'm', 'a', 'g', 'e', '\n' }; |
| 71 | + char plain[] = "/tmp/okm-plain-XXXXXX"; |
| 72 | + check(place(plain, text, sizeof text), "a file of text is written"); |
| 73 | + const int e = start(plain); |
| 74 | + // A file with no `#!' and no image header: the kernel's ENOEXEC. |
| 75 | + check(e == kal_err_not_program, "a file that is no image is not a program"); |
| 76 | + ::unlink(plain); |
| 77 | + |
| 78 | + if (failures == 0) std::printf("openkal-macos: a file this machine cannot start is not a program\n"); |
| 79 | + return failures == 0 ? 0 : 1; |
| 80 | +} |
0 commit comments