/* * Copyright (C) 2009 Lars Wirzenius * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include /* Number of bits to corrupt. */ static int num_bits = 1; /* Choose a random integer in [0..n-1]. */ static long choose(long n) { if (n == 0) return 0; long buckets = (RAND_MAX + 1L) / n; long max = buckets * n; long r; do { r = random(); } while (r >= max); return r / buckets; } static int seek(const char *filename, int fd, off_t offset) { if (lseek(fd, offset, SEEK_SET) == (off_t) -1) { fprintf(stderr, "Can't seek in %s: %s", filename, strerror(errno)); return -1; } return 0; } static int corrupt_one_bit(const char *filename, int fd, struct stat st) { long offset; long bit; unsigned char byte; offset = choose(st.st_size); bit = choose(CHAR_BIT); if (seek(filename, fd, offset) == -1) return -1; if (read(fd, &byte, 1) == -1) { fprintf(stderr, "Can't read %s: %s", filename, strerror(errno)); return -1; } /* This is where we toggle the bit. */ unsigned mask = 1 << bit; // All zeroes, except for the interesting bit. unsigned interesting = byte & mask; // Only the interesting bit can be 1. unsigned toggled = interesting ^ mask; // Interesting bit has been toggled. unsigned others = byte & (~mask); // Interesting bit is 0. byte = others | toggled; /* Now write it back. */ if (seek(filename, fd, offset) == -1) return -1; if (write(fd, &byte, 1) != 1) { fprintf(stderr, "Can't write %s: %s", filename, strerror(errno)); return -1; } return 0; } static int corrupt(const char *filename) { int fd; struct stat st; fd = open(filename, O_RDWR | O_LARGEFILE); if (fd == -1) { fprintf(stderr, "Can't open %s: %s\n", filename, strerror(errno)); return -1; } if (fstat(fd, &st) == -1) { fprintf(stderr, "Can't find length of %s: %s", filename, strerror(errno)); return -1; } for (int i = 0; i < num_bits; ++i) if (corrupt_one_bit(filename, fd, st) == -1) return -1; if (close(fd) == -1) { fprintf(stderr, "Can't close %s: %s", filename, strerror(errno)); return -1; } return 0; } static int parse_args(int argc, char **argv) { const char optstring[] = "n:"; const struct option longopts[] = { { "bits", required_argument, NULL, 'n' }, { 0 }, }; char *endptr; for (;;) { int opt = getopt_long(argc, argv, optstring, longopts, NULL); switch (opt) { case -1: return optind; case 'n': num_bits = strtol(optarg, &endptr, 10); if (*endptr != '\0') { fprintf(stderr, "Invalid number of bits: %s\n", optarg); exit(1); } break; default: case '?': exit(1); } } } int main(int argc, char **argv) { int exit_code; int i; i = parse_args(argc, argv); if (i == -1) return 1; srandom(time(NULL) + getpid()); exit_code = 0; for (; i < argc; ++i) if (corrupt(argv[i]) == -1) exit_code = 1; return exit_code; }