aboutsummaryrefslogtreecommitdiff
path: root/bitsquat
diff options
context:
space:
mode:
authorDavid Timber <dxdt@dev.snart.me>2024-11-06 13:15:43 +0100
committerDavid Timber <dxdt@dev.snart.me>2024-11-06 13:15:43 +0100
commit12645f4533216d4f0d42708faadc789888640ac2 (patch)
tree611d40f72caeb9aa3e15706653187f7d7138b4b2 /bitsquat
parent0bf2a61aed938df9a2dd5188818255c977d4a77b (diff)
Add bitsquat
Diffstat (limited to 'bitsquat')
-rw-r--r--bitsquat/Makefile13
-rw-r--r--bitsquat/bitsquat.c302
2 files changed, 315 insertions, 0 deletions
diff --git a/bitsquat/Makefile b/bitsquat/Makefile
new file mode 100644
index 0000000..aaa47ec
--- /dev/null
+++ b/bitsquat/Makefile
@@ -0,0 +1,13 @@
+CC ?= cc
+CC_OPTS ?= -Wall -Wextra -g
+CC_OPTS_BT ?= -O0
+
+CC_CMD = $(CC) $(CC_OPTS) $(CC_OPTS_BT) $(CC_OPTS_EXTRA)
+
+all: bitsquat
+
+clean:
+ rm -f bitsquat
+
+bitsquat: bitsquat.c
+ $(CC_CMD) -o bitsquat bitsquat.c
diff --git a/bitsquat/bitsquat.c b/bitsquat/bitsquat.c
new file mode 100644
index 0000000..ea421ba
--- /dev/null
+++ b/bitsquat/bitsquat.c
@@ -0,0 +1,302 @@
+#define _POSIX_C_SOURCE 199309L
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define ARGV0 "bitsquat"
+
+
+static struct {
+ union {
+ bool help:1;
+ } flags;
+ size_t size;
+ double report_int;
+ uint32_t fault_p;
+} opts;
+
+static struct {
+ int fd;
+} g;
+
+static void init_opts (void) {
+ opts.report_int = 1.0;
+}
+
+static void init_g (void) {
+ g.fd = -1;
+}
+
+static bool parse_opts (const int argc, const char **argv) {
+ int fr;
+ double tmp_f;
+
+ while (true) {
+ fr = getopt(argc, (char*const*)argv, "hs:F:i:");
+ if (fr == -1) {
+ break;
+ }
+
+ switch (fr) {
+ case 'h': opts.flags.help = true; break;
+ case 's':
+ opts.size = 0;
+ fr = sscanf(optarg, "%zu", &opts.size);
+ if (fr != 1 || opts.size == 0) {
+ fprintf(stderr, ARGV0 ": invalid -s option: %s\n", optarg);
+ return false;
+ }
+ break;
+ case 'F':
+ tmp_f = 0.0;
+ fr = sscanf(optarg, "%lf", &tmp_f);
+ if (fr != 1) {
+ fprintf(stderr, ARGV0 ": invalid -F option: %s\n", optarg);
+ return false;
+ }
+
+ opts.fault_p = (uint32_t)(tmp_f * UINT32_MAX);
+ break;
+ case 'i':
+ opts.report_int = NAN;
+ fr = sscanf(optarg, "%lf", &opts.report_int);
+ // accept "inf" for disabling report
+ if (fr != 1 || isnan(opts.report_int)) {
+ fprintf(stderr, ARGV0 ": invalid -i option: %s\n", optarg);
+ return false;
+ }
+ break;
+ case '?':
+ return false;
+ }
+ }
+
+ if (opts.size == 0) {
+ fprintf(stderr, ARGV0 ": -s option required\n");
+ return false;
+ }
+
+ return true;
+}
+
+static void print_help (void) {
+ // TODO
+}
+
+static bool alloc_g (void) {
+ g.fd = open("/dev/urandom", O_RDONLY);
+
+ if (g.fd < 0) {
+ perror(ARGV0 ": /dev/urandom");
+ return false;
+ }
+
+ return true;
+}
+
+static void dealloc_g (void) {
+ if (g.fd >= 0) {
+ close(g.fd);
+ g.fd = -1;
+ }
+}
+
+static void do_report (const struct timespec *ts, const size_t itc) {
+ fprintf(
+ stderr, ARGV0 ": %10zu.%06ld %20zu\n",
+ (size_t)ts->tv_sec,
+ ts->tv_nsec / 1000,
+ itc);
+}
+
+static void ts_sub (
+ struct timespec *c,
+ const struct timespec *a,
+ const struct timespec *b)
+{
+ c->tv_nsec = a->tv_nsec - b->tv_nsec;
+ c->tv_sec = a->tv_sec - b->tv_sec;
+ if (c->tv_nsec < 0) {
+ c->tv_sec -= 1;
+ c->tv_nsec += 1000000000;
+ }
+}
+
+static double ts2d (const struct timespec *ts) {
+ return ts->tv_nsec / 1000000000 + ts->tv_sec;
+}
+
+static void dump_error (
+ const size_t itc,
+ const struct timespec *ts,
+ const uint8_t *a,
+ const uint8_t *b,
+ const size_t l)
+{
+ printf(
+ "iteration: %10zu.%06ld %zu\n",
+ (size_t)ts->tv_sec,
+ ts->tv_nsec / 1000,
+ itc);
+
+ for (size_t i = 0; i < l; i += 1) {
+ if (a[i] == b[i]) {
+ continue;
+ }
+
+ printf(" - offset: %zu\n", i);
+ printf(" a: 0x%X\n", a[i]);
+ printf(" b: 0x%X\n", b[i]);
+ }
+}
+
+static void getrnd (void *out, const size_t l) {
+ const ssize_t fr = read(g.fd, out, l);
+
+ if (fr < 0) {
+ perror(ARGV0 ": getrnd()");
+ abort();
+ }
+ if ((size_t)fr != l) {
+ abort();
+ }
+}
+
+static bool cointoss (void) {
+ uint32_t rnd;
+
+ if (opts.fault_p == 0) {
+ return false;
+ }
+
+ getrnd(&rnd, sizeof(rnd));
+
+ return opts.fault_p >= rnd;
+}
+
+static void inject_fault (uint8_t *m, const size_t l) {
+ size_t rnd;
+
+ getrnd(&rnd, sizeof(rnd));
+ rnd %= l;
+
+ m[rnd] = ~m[rnd];
+}
+
+static char *getctime (void) {
+ const time_t t = time(NULL);
+ return ctime(&t);
+}
+
+static int do_main (void) {
+ int ec = 0;
+ int fr;
+ void *m1 = NULL;
+ void *m2 = NULL;
+ const size_t s = opts.size;
+ struct {
+ struct timespec tp_start;
+ struct timespec tp_now;
+ struct timespec tp_report;
+ struct timespec d_start;
+ struct timespec d_report;
+ } ts;
+ size_t itc = 0; // iteration counter
+ int bp; // bit pattern
+
+ fprintf(stderr, ARGV0 ": allocating (%zu * 2) bytes of memory ...\n", s);
+
+ m1 = malloc(s);
+ m2 = malloc(s);
+ if (m1 == NULL || m2 == NULL) {
+ perror(ARGV0);
+ goto END;
+ }
+
+ bp = 0xCD;
+ memset(m1, bp, s);
+ memset(m2, bp, s);
+
+ fprintf(stderr, ARGV0 ": loop start: %s\n", getctime());
+
+ clock_gettime(CLOCK_MONOTONIC, &ts.tp_start);
+ ts.tp_report = ts.tp_start;
+
+ while (true) {
+ switch (itc % 2) {
+ case 0: bp = 0x55; break;
+ case 1: bp = 0xAA; break;
+ }
+
+ memset(m1, bp, s);
+ memset(m2, bp, s);
+ if (cointoss()) {
+ inject_fault((uint8_t*)m1, s);
+ }
+ if (cointoss()) {
+ inject_fault((uint8_t*)m2, s);
+ }
+ fr = memcmp(m1, m2, s);
+ itc += 1;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts.tp_now);
+ ts_sub(&ts.d_start, &ts.tp_now, &ts.tp_start);
+ ts_sub(&ts.d_report, &ts.tp_now, &ts.tp_report);
+
+ if (fr != 0) {
+ dump_error(
+ itc,
+ &ts.d_start,
+ (const uint8_t*)m1,
+ (const uint8_t*)m2,
+ s);
+ ec = 1;
+ goto END;
+ }
+
+ if (ts2d(&ts.d_report) >= opts.report_int) {
+ do_report(&ts.d_start, itc);
+ ts.tp_report = ts.tp_now;
+ }
+ }
+
+END:
+ free(m1);
+ free(m2);
+ return ec;
+}
+
+int main (const int argc, const char **argv) {
+ int ec = 0;
+
+ init_opts();
+ init_g();
+
+ if (!parse_opts(argc, argv)) {
+ ec = 2;
+ goto END;
+ }
+
+ if (opts.flags.help) {
+ print_help();
+ goto END;
+ }
+
+ if (!alloc_g()) {
+ ec = 1;
+ goto END;
+ }
+
+ ec = do_main();
+
+END:
+ dealloc_g();
+ return ec;
+}