/* Buffer overflow demo */ /* Last edited on 2009-01-09 04:33:11 by stolfi */ /* Lines with an !ABC! comment go only to version A,B, or C. */ /*!0!*/ /* Lines with an ?1? are for debugging during development. */ /*!0!*/ /* Stage 0: reference version for "diff" (does not compile). */ /*!01!*/ /* Stage 1: all bugs (segmentation fault) */ /*!01!*/ /* Stage 2: fixed name length bug (lists /etc/passwd) */ /*!02!*/ /* Stage 3: fixed name length bug and imgbuf length bug */ /*!03!*/ #include #include #include #include #include #include /* COMMAND LINE SYNTAX AND EXAMPLES */ static char *help0 = "demo [ -v ] [ -n REPEAT ] [ -z | -r ] IMAGENAME"; static char *help1 = " To show \"serra.img\" three times: \"demo -n 3 -r serra\""; static char *help2 = " To show \"lula.img.zz\" five times: \"demo -n 5 -z lula\""; static char *help3 = " The \"-v\" option prints diagnostics."; /* Define this as 1 to get diagnostics: */ #define DEBUG_MEMORY 1 /*?1?*/ #define DEBUG_MEMORY 0 /*?0?*/ /* FUNCTION DECLARATIONS */ /* Parses the command line arguments, saves the repeat count in {*repeat}, saves the image type ('r' or 'z') in {*kind}, and stores the full file name in {fname[...]}: */ void parse_args(int argc, char **argv, unsigned int *repeat, char *kind, char *fname); /* Reads the image file {fname} (which should be of type {kind}), decodes it (using the line-buffer {buf[...]}), and writes it to {stdout}: */ void show_file(char *fname, char kind, char *buf); /* Prints the current date: */ void show_date(char *msg); /* Waits for a prescribed number of cycles: */ void wait(int cycles); /* Error in command line: prints {msg}, {usage}, and halts the program. */ void arg_error(char *arg, char *msg); /* Error in image file: prints {msg} and halts the program. */ void file_error(char *msg); /* Shows the memory layout of the program's main variables: */ void show_memory ( char *title, int *jP, unsigned int *repeatP, char *kindP, char *fnameP, int fnameN, char *bufP, int bufN ); /* PROGRAM LIMITS */ /* Maximum image name length: */ /*!01!*/ #define MAX_NAME_LENGTH 9 /*!01!*/ /*!01!*/ /* Max length of image filename (with final '\0'): */ /*!01!*/ #define FNAME_SIZE (MAX_NAME_LENGTH+1) /*!01!*/ /* Max length of image's bare name (without ".img" and ".zz"): */ /*!023!*/ #define MAX_NAME_LENGTH 9 /*!023!*/ /*!023!*/ /* Max length of image filename (with ".img.zz" and final '\0'): */ /*!023!*/ #define FNAME_SIZE (MAX_NAME_LENGTH+8) /*!023!*/ /* Maximum image dimensions: */ #define MAXROWS 600 #define MAXCOLS 800 /* Buffer size for image input (= MAXCOLS + 1) */ #define BUF_SIZE 80l /*!012!*/ #define BUF_SIZE 801 /*!03!*/ /* Maximum repeat count */ #define MAX_REPEAT 10 /* IMPLEMENTATIONS */ int main(int argc, char **argv) { char fname[FNAME_SIZE]; unsigned int repeat; char kind; char buf[BUF_SIZE]; int j; /* Show the layout and contents of the variables in memory: */ show_memory("init", &j, &repeat, &kind, fname, FNAME_SIZE, buf, BUF_SIZE); /* Parse command line arguments */ parse_args(argc, argv, &repeat, &kind, fname); /* Now flash the image {repeat} times on the terminal: */ for (j = 0; j < repeat; j++) { /* Show again the layout and contents of the variables in memory: */ show_memory("loop", &j, &repeat, &kind, fname, FNAME_SIZE, buf, BUF_SIZE); if (! DEBUG_MEMORY) { /* Clear the screen */ printf("\033[H\033[2J\n"); wait(30000000); } /* Flash the picture: */ show_file(fname, kind, &(buf[0])); wait(100000000); } /* Show the layout and contents of the variables in memory: */ show_memory("fine", &j, &repeat, &kind, fname, FNAME_SIZE, buf, BUF_SIZE); show_date("demo finished on"); return(0); } void parse_args(int argc, char **argv, unsigned int *repeat, char *kind, char *fname) { /* Defaults: */ *kind = 'r'; *repeat = 1; *fname = '\000'; int i; for (i = 1; i < argc; i++) { fprintf(stderr, "--- argv[%d] = %s ---\n", i, argv[i]); /*?1?*/ /*?1?*/ /* Parse command line argument number {i}: */ if (strcmp(argv[i],"-z") == 0) { /* Image file is compressed: */ *kind = 'z'; } else if (strcmp(argv[i],"-r") == 0) { /* Image file is plain text: */ *kind = 'r'; } else if (strcmp(argv[i],"-n") == 0) { /* The next argument is the repeat count: */ char *rest; int rep; i++; if (i >= argc) { arg_error(argv[i], "no repeat count"); } rep = strtol(argv[i], &rest, 10); if ((*rest != '\0') || (rep < 0) || (rep > MAX_REPEAT)) { arg_error(argv[i], "invalid repeat count"); } *repeat = (int)rep; } else if (argv[i][0] == '-') { arg_error(argv[i], "unrecognized option"); } else { /* {argv[i]} must be the image's name (without extensions). */ /* Check name length (including final '\0'): */ /*!01!*/ /* Check name length: */ /*!023!*/ if (strlen(argv[i]) > MAX_NAME_LENGTH+1) /*!01!*/ if (strlen(argv[i]) > MAX_NAME_LENGTH) /*!023!*/ { arg_error(argv[i], "image name is too long"); } else { fprintf(stderr, "building the file name..."); /*?1?*/ /*?1?*/ strcpy(fname, argv[i]); strcat(fname, ".img"); if (*kind == 'z') { strcat(fname, ".zz"); } /*?1?*/ fprintf(stderr, "done.\n"); /*?1?*/ fprintf(stderr, "fname = \"%s\"\n", fname); /*?1?*/ fprintf(stderr, "kind = '%c'\n", *kind); /*?1?*/ fprintf(stderr, "repeat = %0x\n", *repeat); /*?1?*/ } } } } void show_file(char *fname, char kind, char *buf) { /* Read an ascii-art image from the given {fname} and prints it to standard out. If the {kind} argument is {r} ("raw"), the file is printed as is. Otherwise, the file is assumed to be in a certain compressed format, and is uncompressed before being printed. Currently, the only kind method supported is a variant of run-length kind ({kind = 'z'}). In this kind, each line of the file is a row (line) of the image. Each input line consists of a series of "groups". Each group consists of one "control" byte, possibly followed by a data byte. The control byte consists of a {type} bit and a seven-bit {val} field. If `type == 1', then the byte {val + 32}is inserted as the next image file pixel. If {type == 0}, then the following byte of the file is taken to be a image file pixel which is to be replicated {val + 1} times. The caller must provide a buffer {buf} with at least MAXCOLS+1 characters (since the maximum uncompressed line has MAXCOLS pixels plus a final '\0'). */ int c, d, type, count; int col; FILE *f; /*?1?*/ fprintf(stderr, "\n"); /*?1?*/ fprintf(stderr, "entering %s\n", __FUNCTION__); /*?1?*/ fprintf(stderr, "fname = \"%s\"\n", fname); /*?1?*/ fprintf(stderr, "kind = '%c'\n", kind); /*?1?*/ f = fopen(fname, "r"); if (f == NULL) { file_error("file not found"); } while((c = fgetc(f)) != EOF) { /* Reads a new row of the image, stores it into {buf}: */ col = 0; while(c != '\n') { switch(kind) { case 'r': /* Raw format: */ if (col >= MAXCOLS) { file_error("too many pixels in row"); } else { buf[col] = c; col++; } break; case 'z': /* Compressed format: */ type = (c >> 7); if (type == 1) { /* Verbatim pixel: */ if (col >= MAXCOLS) { file_error("too many pixels in row"); } else { buf[col] = (c & 127) + 32; col++; } } else { /* Replication group: */ if (((d = fgetc(f)) == EOF) || ( d == '\n')) { file_error("premature EOF/EOL in replicated group"); } count = (c & 127) + 1; while(count > 0) { if (col >= MAXCOLS) { file_error("too many pixels in row"); } else { buf[col] = d; col++; count--; } } } break; default: fprintf(stderr, "kind = '%c'\n", kind); file_error("invalid kind code"); } c = fgetc(f); if (c == EOF) { file_error("missing end-of-line"); } } /* Terminate {buf} with a null byte: */ buf[col] = '\000'; printf("%s\n", buf); } /*?1?*/ fprintf(stderr, "fname = \"%s\"\n", fname); /*?1?*/ fprintf(stderr, "kind = '%c'\n", kind); /*?1?*/ fprintf(stderr, "exiting %s\n", __FUNCTION__); /*?1?*/ fprintf(stderr, "\n"); /*?1?*/ } void show_date(char *msg) { time_t *now = (time_t *)malloc(sizeof(time_t)); (void)time(now); printf("%s%s\n", msg, ctime(now)); free(now); } void wait(int cycles) { int i; int j = 0; for (i = 0; i < cycles; i++) { j = 1 - j; } } void arg_error(char *arg, char *msg) { fprintf(stderr, "** %s", msg); if (arg != NULL) { fprintf(stderr, " (arg = \"%s\")", arg); } fprintf(stderr, "\n"); fprintf(stderr, "%s\n", help0); fprintf(stderr, "%s\n", help1); fprintf(stderr, "%s\n", help2); fprintf(stderr, "%s\n", help3); exit(1); } void file_error(char *msg) { fprintf(stderr, "** %s\n", msg); exit(1); } void show_memory ( char *title, int *jP, unsigned int *repeatP, char *kindP, char *fname, int fnameN, char *buf, int bufN ) { if (! DEBUG_MEMORY) { return; } int MAXVARS = 20; char *name[MAXVARS]; void *addr[MAXVARS]; size_t size[MAXVARS]; int nv = 0; /* Counts variables being shown. */ auto void show_var(char *nm, void *ad, size_t sz); void show_var(char *nm, void *ad, size_t sz) { assert(nv < MAXVARS); name[nv] = nm; addr[nv] = ad; size[nv] = sz; nv++; } show_var("j", jP, sizeof(int)); show_var("repeat", repeatP, sizeof(unsigned int)); show_var("kind", kindP, sizeof(char)); show_var("fname", fname, fnameN*sizeof(char)); show_var("buf", buf, bufN*sizeof(char)); /* Sort by address: */ int r, s; for (r = 0; r < nv; r++) { for (s = r+1; s < nv; s++) { int64_t adr = (size_t)(addr[r]); int64_t ads = (size_t)(addr[s]); if (ads < adr) { { char *t = name[r]; name[r] = name[s]; name[s] = t; } { void *t = addr[r]; addr[r] = addr[s]; addr[s] = t; } { size_t t = size[r]; size[r] = size[s]; size[s] = t; } } } } /* Get the span of the given variables: */ char *base = (char *)addr[0]; char *btop = (char *)(addr[nv-1]) + size[nv-1]; int64_t bnum = (btop - base); /* Print the memory map: */ fprintf(stderr, "--- memory layout (%s) ---\n", title); fprintf(stderr, "spanning %lld", (int64_t)(size_t)base); fprintf(stderr, " ... %lld\n", (int64_t)(size_t)(btop-1)); fprintf(stderr, " ( %lld bytes )\n", bnum); auto void show_chunk(char *nm, char *ad, size_t sz, char *bs); void show_chunk(char *nm, char *ad, size_t sz, char *bs) { int64_t lo = ad - bs; int64_t hi = lo + sz - 1; fprintf(stderr, "%-10s %4lld .. %4lld ( %4d )", nm, lo, hi, sz); int s; fprintf(stderr, " ="); /* Print sub-chunks of 10 bytes: */ for (s = 0; s < sz; s += 10) { /* Skip middle part of very long chunks: */ if ((s == 30) && (sz > 100)) { s = 10 * ((sz / 10) - 4); if (s <= 30) { s = 40; } fprintf(stderr, "%35s%3s%27s %3s%7s\n", "", "...", "", "...", ""); } /* Align to previous sub-chunk: */ if (s > 0) { fprintf(stderr, "%35s", ""); } /* Dump the sub-chunk in hex: */ int t; for (t = 0; t < 10; t++) { if (s + t < sz) { unsigned int c = ((unsigned char *)ad)[s + t]; fprintf(stderr, " %02x", c); } else { fprintf(stderr, " "); } } fprintf(stderr, " "); /* Dump the sub-chunk in char: */ for (t = 0; t < 10; t++) { if (s + t < sz) { unsigned int c = ((unsigned char *)ad)[s + t]; if (c <= '\037') { c = '?'; } if ((c >= '\177') && (c <= '\237')) { c = '?'; } if (c == '\240') { c = ' '; } if (c == '\255') { c = '-'; } fprintf(stderr, "%c", c); } else { fprintf(stderr, " "); } } fprintf(stderr, "\n"); } } char *next = base; for (r = 0; r < nv; r++) { char *ad = (char *)(addr[r]); size_t sz = size[r]; assert(next <= ad); if (next < ad) { show_chunk("???", next, ad - next, base); } show_chunk(name[r], ad, sz, base); next = ad + sz; } }