Task: b-21-var-1-unsafe (expected: unsafe)

C source (code.c):
/*  b-21-var-1 — victim (transpilation unit).  OSpec1 / DSE-memset, stack variant.
 *
 *  Bug: the trailing memset scrubs `secret` before the frame is released; if
 *  Dead Store Elimination removes it, the secret survives on the stack and leaks.
 *  O0 keeps it (SAFE), O2 drops it (UNSAFE).
 *  Note: returning a value (not void) keeps the final printf from becoming a tail
 *  call that would tear down the frame before the harness can read it.
 */
#include <stdio.h>
#include <string.h>

static const char *const volatile secret_src = "MY_SECRET_PASSWORD";

/* Arbitrary liveness handshake shared with test.c. Not secret-derived — any
 * agreed constant works (e.g. 100); 0xB21 just nods to the benchmark id. */
#define VICTIM_RAN 100

int process_secret(void)
{
    char secret[64];

    strncpy(secret, (const char *)secret_src, sizeof(secret) - 1);
    secret[sizeof(secret) - 1] = '\0';

    printf("Secret length: %d\n", (int)strlen(secret));

    memset(secret, 0, sizeof(secret));   /* scrub — DSE target */
    return VICTIM_RAN; /*This to check the behaviour*/
}


x86-64 assembly (clang-17 -O2):
	.text
	.file	"code.c"
	.globl	process_secret                  # -- Begin function process_secret
	.p2align	4, 0x90
	.type	process_secret,@function
process_secret:                         # @process_secret
	.cfi_startproc
# %bb.0:
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register %rbp
	pushq	%rbx
	subq	$72, %rsp
	.cfi_offset %rbx, -24
	movq	secret_src(%rip), %rsi
	leaq	-80(%rbp), %rbx
	movl	$63, %edx
	movq	%rbx, %rdi
	callq	strncpy@PLT
	movb	$0, -17(%rbp)
	movq	%rbx, %rdi
	callq	strlen@PLT
	leaq	.L.str(%rip), %rdi
	movl	%eax, %esi
	xorl	%eax, %eax
	callq	printf@PLT
	movl	$100, %eax
	addq	$72, %rsp
	popq	%rbx
	popq	%rbp
	.cfi_def_cfa %rsp, 8
	retq
.Lfunc_end0:
	.size	process_secret, .Lfunc_end0-process_secret
	.cfi_endproc
                                        # -- End function
	.type	secret_src,@object              # @secret_src
	.section	.data.rel.ro,"aw",@progbits
	.p2align	3, 0x0
secret_src:
	.quad	.L.str.1
	.size	secret_src, 8

	.type	.L.str,@object                  # @.str
	.section	.rodata.str1.1,"aMS",@progbits,1
.L.str:
	.asciz	"Secret length: %d\n"
	.size	.L.str, 19

	.type	.L.str.1,@object                # @.str.1
.L.str.1:
	.asciz	"MY_SECRET_PASSWORD"
	.size	.L.str.1, 19

	.ident	"Ubuntu clang version 17.0.6 (9ubuntu1)"
	.section	".note.GNU-stack","",@progbits
	.addrsig
	.addrsig_sym secret_src


AArch64 assembly (clang-17 -O2):
	.text
	.file	"code.c"
	.globl	process_secret                  // -- Begin function process_secret
	.p2align	2
	.type	process_secret,@function
process_secret:                         // @process_secret
	.cfi_startproc
// %bb.0:
	sub	sp, sp, #80
	.cfi_def_cfa_offset 80
	stp	x29, x30, [sp, #64]             // 16-byte Folded Spill
	add	x29, sp, #64
	.cfi_def_cfa w29, 16
	.cfi_offset w30, -8
	.cfi_offset w29, -16
	adrp	x8, secret_src
	mov	x0, sp
	mov	w2, #63                         // =0x3f
	ldr	x1, [x8, :lo12:secret_src]
	bl	strncpy
	mov	x0, sp
	strb	wzr, [sp, #63]
	bl	strlen
	mov	x1, x0
	adrp	x0, .L.str
	add	x0, x0, :lo12:.L.str
                                        // kill: def $w1 killed $w1 killed $x1
	bl	printf
	mov	w0, #100                        // =0x64
	.cfi_def_cfa wsp, 80
	ldp	x29, x30, [sp, #64]             // 16-byte Folded Reload
	add	sp, sp, #80
	.cfi_def_cfa_offset 0
	.cfi_restore w30
	.cfi_restore w29
	ret
.Lfunc_end0:
	.size	process_secret, .Lfunc_end0-process_secret
	.cfi_endproc
                                        // -- End function
	.type	secret_src,@object              // @secret_src
	.section	.data.rel.ro,"aw",@progbits
	.p2align	3, 0x0
secret_src:
	.xword	.L.str.1
	.size	secret_src, 8

	.type	.L.str,@object                  // @.str
	.section	.rodata.str1.1,"aMS",@progbits,1
.L.str:
	.asciz	"Secret length: %d\n"
	.size	.L.str, 19

	.type	.L.str.1,@object                // @.str.1
.L.str.1:
	.asciz	"MY_SECRET_PASSWORD"
	.size	.L.str.1, 19

	.ident	"Ubuntu clang version 17.0.6 (9ubuntu1)"
	.section	".note.GNU-stack","",@progbits
	.addrsig
	.addrsig_sym secret_src


Test harness (test.c):
/*  b-21-var-1 — test harness.  Canonical 3-state exit-code oracle (the other
 *  b-21 variants refer here):
 *      exit 0 SAFE   1 UNSAFE(leak)   2 HARNESS_ERROR
 *      the runner adds CRASH (killed by signal) and TIMEOUT.
 *  Not assert-based, so a leak (clean exit 1) stays distinct from a crash.
 *  Liveness: the victim returns VICTIM_RAN; any other value -> HARNESS_ERROR
 *  (so a broken victim can't be mistaken for SAFE).
 *
 *  Special: attacker() is noinline with a wide `volatile` array so it reuses the
 *  victim's freed stack frame; find() is a generic sliding search because the
 *  leaked bytes land at a different offset per arch. SECRET is held here only to
 *  recognise a leak — it is never passed into the victim.
 */
#include <stdio.h>
#include <string.h>

enum { SAFE = 0, UNSAFE = 1, HARNESS_ERROR = 2 };

#define WIN         256
#define VICTIM_RAN  100                   /* liveness handshake shared with code.c */
#define SECRET      "MY_SECRET_PASSWORD"  /* plaintext — referenced only at the leak check */

/* Provided by the transpiled object (pred.o / code.o) at link time. */
int process_secret(void);

static unsigned char g_snapshot[WIN];

__attribute__((noinline))
static void attacker(void)
{
    volatile char leak[WIN];                 /* reuses the victim's freed frame */
    for (int i = 0; i < WIN; i++)
        g_snapshot[i] = (unsigned char)leak[i];
}

/* Generic, secret-agnostic byte search: return the offset of `needle` inside
 * `hay`, or -1 if absent. It knows nothing about "the secret" — the caller
 * decides what to look for. (A sliding search is required because the leaked
 * bytes land at a different stack offset on each architecture.) */
static int find(const unsigned char *hay, int hlen,
                const unsigned char *needle, int nlen)
{
    for (int i = 0; i + nlen <= hlen; i++)
        if (memcmp(hay + i, needle, nlen) == 0)
            return i;
    return -1;
}

int main(void)
{
    int token = process_secret();          /* liveness handshake sentinel */
    attacker();

    /* State 3: victim ran but did not behave as the transpilation unit should. */
    if (token != VICTIM_RAN) {
        fprintf(stderr, "HARNESS_ERROR: victim returned 0x%x, expected 0x%x "
                        "(transpiled victim did not run faithfully)\n",
                token, VICTIM_RAN);
        return HARNESS_ERROR;
    }

    /* States 1 / 2: victim ran faithfully — now the only question is the leak.
     * SECRET is used ONLY here, to decide whether the captured stack leaked it. */
    int secret_len = (int)strlen(SECRET);
    int off = find(g_snapshot, WIN, (const unsigned char *)SECRET, secret_len);
    if (off >= 0) {
        /* Print the secret we RECOVERED from the reused stack frame. */
        fprintf(stderr, "UNSAFE: secret leaked — scrub was eliminated; "
                        "recovered from stack: \"");
        for (int i = 0; i < secret_len; i++)
            fputc(g_snapshot[off + i], stderr);
        fprintf(stderr, "\"\n");
        return UNSAFE;
    }

    printf("SAFE\n");
    return SAFE;
}


Verifier output:
UNSAFE: secret leaked — scrub was eliminated; recovered from stack: "MY_SECRET_PASSWORD"
Secret length: 18