/* DF-2366: rc4_init (sys/crypto/rc4/rc4.c:58-77) lacks a keylen>0 guard.
 * The key-schedule loop is
 *
 *   for (j = i = k = 0; i < 256; i++) {
 *           j += state->perm[i] + key[k];        <-- reads key[0] ...
 *           swap_bytes(&state->perm[i], &state->perm[j]);
 *           if (++k >= keylen)                   <-- keylen<=0: k stays 0
 *                   k = 0;
 *   }
 *
 * so a non-positive keylen reads key[0] (and only key[0]) 256 times; if the
 * caller passed a zero-length key buffer, key[0] is out of bounds.
 *
 * This is a userland replica of the exact loop (no kernel headers needed)
 * with key[] placed at the very end of a page followed by a PROT_NONE page,
 * so the OOB read of key[0] faults deterministically.  In the kernel the
 * same instruction reads one byte past the caller-supplied buffer. */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>

struct rc4_state { u_char perm[256]; int index1, index2; };

static __inline void swap_bytes(u_char *a, u_char *b){ u_char t=*a; *a=*b; *b=t; }

/* verbatim port of rc4_init loop with the guard REMOVED (as shipped) */
static void rc4_init_buggy(struct rc4_state *const state,
			   const u_char *key, int keylen)
{
	u_char j; int i, k;
	for (i = 0; i < 256; i++) state->perm[i] = (u_char)i;
	state->index1 = 0; state->index2 = 0;
	for (j = i = k = 0; i < 256; i++) {
		j += state->perm[i] + key[k];		/* <-- key[0] read */
		swap_bytes(&state->perm[i], &state->perm[j]);
		if (++k >= keylen) k = 0;			/* keylen<=0: k=0 */
	}
}

int main(void)
{
	struct rc4_state st;
	u_char *pageend;

	pageend = mmap(NULL, 2*4096, PROT_READ|PROT_WRITE,
		       MAP_PRIVATE|MAP_ANON, -1, 0);
	if (pageend == MAP_FAILED) { perror("mmap"); return 1; }
	if (mprotect(pageend + 4096, 4096, PROT_NONE) != 0) { perror("mprotect"); return 1; }

	printf("[*] zero-length key buffer modeled by a pointer whose first byte\n"
	       "    (key[0]) lies in a PROT_NONE guard page\n");
	printf("[*] calling buggy rc4_init(state, key, keylen=0)...\n");
	fflush(stdout);
	rc4_init_buggy(&st, pageend + 4096, 0);       /* zero-length key: key[0] lands in the guard page */
	printf("[-] returned without reading key[0]?? not reproduced\n");
	return 1;
}
