TIL: Read sysfs without root access in Linux

Abhishek Chanda

  ·  2 min read

Typically an user needs root access to be able to read sysfs data. But such root access might not be possible in all case; an example being Postgres which cannot be run as root. I recently learned that it is possible to set an extra capability to executables such that it can read sysfs while not being root.

I wrote a simple C program to read a specific sysfs entry

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#define SYSFS_PATH "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:0/energy_uj"
#define BUFFER_SIZE 32

int main() {
    int fd;
    char buffer[BUFFER_SIZE];
    ssize_t bytes_read;
    unsigned long long energy_uj;

    // Open the sysfs file
    fd = open(SYSFS_PATH, O_RDONLY);
    if (fd == -1) {
        fprintf(stderr, "Error opening file: %s\n", strerror(errno));
        return 1;
    }

    // Read the contents of the file
    bytes_read = read(fd, buffer, BUFFER_SIZE - 1);
    if (bytes_read == -1) {
        fprintf(stderr, "Error reading file: %s\n", strerror(errno));
        close(fd);
        return 1;
    }

    // Null-terminate the buffer
    buffer[bytes_read] = '\0';

    // Close the file descriptor
    close(fd);

    // Convert the string to an unsigned long long
    energy_uj = strtoull(buffer, NULL, 10);

    // Print the result
    printf("Energy (µJ): %llu\n", energy_uj);

    return 0;
}

When I compile and run it as my regular non-root user, I get this

abhishek@guest:~$ id -u
1002
abhishek@guest:~$ cc foo.c
abhishek@guest:~$ ./a.out
Error opening file: Permission denied

But I can set a specific capability on the generated binary

abhishek@guest:~$ sudo setcap cap_dac_read_search=+ep a.out
[sudo] password for abhishek:
abhishek@guest:~$ echo $?
0

And then the access works

abhishek@guest:~$ ./a.out
Energy (µJ): 128875379306