@@ 0,0 1,35 @@
+Reduce the stack footprint of pixman's function
+general_composite_rect() which allocates a large buffer
+`stack_scanline_buffer`. Make it `static __thread` instead.
+
+--- a/pixman/pixman-general.c 2015-12-27 21:37:37.000000000 +0100
++++ b/pixman/pixman-general.c 2016-05-05 12:24:47.346661080 +0200
+@@ -128,8 +128,8 @@
+ pixman_composite_info_t *info)
+ {
+ PIXMAN_COMPOSITE_ARGS (info);
+- uint8_t stack_scanline_buffer[3 * SCANLINE_BUFFER_LENGTH];
+- uint8_t *scanline_buffer = (uint8_t *) stack_scanline_buffer;
++ static __thread uint8_t static_scanline_buffer[3 * SCANLINE_BUFFER_LENGTH];
++ uint8_t *scanline_buffer = (uint8_t *) static_scanline_buffer;
+ uint8_t *src_buffer, *mask_buffer, *dest_buffer;
+ pixman_iter_t src_iter, mask_iter, dest_iter;
+ pixman_combine_32_func_t compose;
+@@ -158,7 +158,7 @@
+ if (width <= 0 || _pixman_multiply_overflows_int (width, Bpp * 3))
+ return;
+
+- if (width * Bpp * 3 > sizeof (stack_scanline_buffer) - 15 * 3)
++ if (width * Bpp * 3 > sizeof (static_scanline_buffer) - 15 * 3)
+ {
+ scanline_buffer = pixman_malloc_ab_plus_c (width, Bpp * 3, 15 * 3);
+
+@@ -232,7 +232,7 @@
+ if (dest_iter.fini)
+ dest_iter.fini (&dest_iter);
+
+- if (scanline_buffer != (uint8_t *) stack_scanline_buffer)
++ if (scanline_buffer != (uint8_t *) static_scanline_buffer)
+ free (scanline_buffer);
+ }
+
@@ 1,16 1,29 @@
#define _XOPEN_SOURCE
#include <errno.h>
-#include <stdlib.h>
+#include <getopt.h>
+#include <limits.h>
+#include <signal.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <pwd.h>
#include <unistd.h>
int chroot(char *path);
-void check(int p, char *ctx) {
+struct userbind {
+ char *src, *dest;
+ struct userbind *next;
+};
+
+pid_t child;
+char *pidpath = NULL, *root = NULL;
+struct userbind *binds = NULL;
+
+static void check(int p, char *ctx) {
if (p) {
return;
}
@@ 18,24 31,173 @@ void check(int p, char *ctx) {
exit(1);
}
+static void usage(void) {
+ fprintf(stderr, "Usage: qemu-chroot [-b <src>:<dest>] [-p <pidfile>] "
+ "/bin/qemu-system-<arch> [args...]\n");
+}
+
+static void cleanup() {
+ int r;
+ char path[PATH_MAX + 1] = "";
+ char *paths[] = {
+ "/dev/null",
+ "/dev/kvm",
+ };
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
+ snprintf(path, sizeof(path), "%s%s", root, paths[i]);
+ r = umount2(path, 0);
+ if (r != 0) {
+ fprintf(stderr, "umount %s: %s\n", path, strerror(errno));
+ }
+ }
+
+ while (binds) {
+ snprintf(path, sizeof(path), "%s%s", root, binds->dest);
+ r = umount2(path, 0);
+ if (r != 0) {
+ fprintf(stderr, "umount %s: %s\n", path, strerror(errno));
+ }
+ struct userbind *next = binds->next;
+ free(binds);
+ binds = next;
+ }
+
+ r = umount2(root, 0);
+ if (r != 0) {
+ fprintf(stderr, "umount %s: %s\n", root, strerror(errno));
+ }
+ remove(root);
+
+ remove(pidpath);
+}
+
+static void sig(int s) {
+ kill(child, s);
+}
+
int main(int argc, char *argv[]) {
+ FILE *pidfile = NULL;
+
+ char c;
+ while ((c = getopt(argc, argv, "b:p:h")) != -1) {
+ switch (c) {
+ case 'b':;
+ char *src = strtok(optarg, ":");
+ char *dest = strtok(NULL, ":");
+ struct userbind *bind = calloc(1, sizeof(struct userbind));
+ bind->src = src;
+ bind->dest = dest;
+ bind->next = binds;
+ binds = bind;
+ break;
+ case 'p':
+ if (pidfile != NULL) {
+ fprintf(stderr, "-p may only be specified once");
+ return 1;
+ }
+ pidpath = optarg;
+ pidfile = fopen(pidpath, "w");
+ check(pidfile != NULL, "open pid file for write");
+ break;
+ case 'h':
+ usage();
+ return 0;
+ default:
+ usage();
+ return 1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ char tmp[] = "/tmp/qemu-chroot.XXXXXX";
+ root = mkdtemp(tmp);
+ check(root != NULL, "mkdtemp");
+
struct passwd *nobody = getpwnam("nobody");
check(nobody != NULL, "getpwnam");
int r;
- r = chroot("/usr/lib/qemu-minimal-static");
- check(r == 0, "chroot");
-
- r = setgid(nobody->pw_gid);
- check(r == 0, "setgid");
- r = setegid(nobody->pw_gid);
- check(r == 0, "setegid");
- r = setuid(nobody->pw_uid);
- check(r == 0, "setuid");
- r = seteuid(nobody->pw_uid);
- check(r == 0, "seteuid");
-
- r = execv(argv[1], &argv[1]);
- check(r == 0, "execv");
- return 1;
+ r = mount("/usr/lib/qemu-minimal-static", root, "", MS_BIND, NULL);
+ check(r == 0, "bind mount /usr/lib/qemu-minimal-static");
+
+ char *paths[] = {
+ "/dev/null",
+ "/dev/kvm",
+ };
+ char path[PATH_MAX + 1] = "";
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
+ snprintf(path, sizeof(path), "%s%s", root, paths[i]);
+ r = mount(paths[i], path, "", MS_BIND, NULL);
+ if (r != 0) {
+ fprintf(stderr, "bind mount %s: %s\n", paths[i], strerror(errno));
+ return 1;
+ }
+ }
+
+ struct userbind *b = binds;
+ while (b) {
+ snprintf(path, sizeof(path), "%s%s", root, b->dest);
+ r = mount(b->src, path, "", MS_BIND, NULL);
+ if (r != 0) {
+ fprintf(stderr, "bind mount %s: %s\n", b->src, strerror(errno));
+ return 1;
+ }
+ b = b->next;
+ }
+
+ struct sigaction sa = {0};
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = sig;
+ sa.sa_flags = SA_RESTART;
+
+ r = sigaction(SIGINT, &sa, NULL);
+ check(r != -1, "sigaction");
+
+ r = sigaction(SIGTERM, &sa, NULL);
+ check(r != -1, "sigaction");
+
+ child = fork();
+ check(child != -1, "fork");
+ if (child == 0) {
+ if (pidfile != NULL) {
+ fclose(pidfile);
+ }
+
+ r = chroot(root);
+ check(r == 0, "chroot");
+
+ r = setgid(nobody->pw_gid);
+ check(r == 0, "setgid");
+ r = setegid(nobody->pw_gid);
+ check(r == 0, "setegid");
+ r = setuid(nobody->pw_uid);
+ check(r == 0, "setuid");
+ r = seteuid(nobody->pw_uid);
+ check(r == 0, "seteuid");
+
+ if (setuid(0) != -1) {
+ fprintf(stderr, "Unable to drop root, bailing out\n");
+ return 1;
+ }
+
+ r = execv(argv[0], &argv[0]);
+ check(r == 0, "execv");
+ }
+
+ if (pidfile != NULL) {
+ fprintf(pidfile, "%d\n", child);
+ fclose(pidfile);
+ }
+
+ int status;
+ do {
+ pid_t wpid = waitpid(child, &status, WUNTRACED | WCONTINUED);
+ check(wpid != -1, "waitpid");
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+
+ cleanup();
+
+ return WEXITSTATUS(status);
}