4
votes

I am trying to power off my machine using the raw reboot system call. Here is my code:

#include <unistd.h>
#include <linux/reboot.h>
#include <stdio.h>
#include <errno.h>

int main(void) {
    int ret = reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL);
    if (ret == -1) {
        printf("error: %d\n", errno);
    }
    return 0;
}

According to the man page, "Only the superuser may call reboot()". So I run the program as root and get the following output:

alex@laptop:~/code$ sudo ./a.out 
[sudo] password for alex: 
error: 22

What is error #22? That would be EINVAL, which according to the reboot man page signifies "Bad magic numbers or cmd". As I'm using the definitions provided by #include <linux/reboot.h>, I can't imagine the values are incorrect, but just in case, I also tried hardcoding the magic number values provided in the man page, to no avail (same error).

However, if I try glibc's wrapper function reboot(LINUX_REBOOT_CMD_POWER_OFF) provided by <sys/reboot.h>, it works fine (instantly shuts down machine).

There's no practical reason why I want to do this, just playing with syscall's and trying to make it work. If it helps, here are some details about the system I'm running this on:

Operating System: Ubuntu 20.04
Kernel: Linux version 5.4.0-53-generic
Compiler: gcc version 9.3.0

Let me know if any additional information might be useful!

3
You really need to read the glibc wrapper’s source code to see how to do it :) It’s all there. Or alternatively look at the kernel source for that syscall.Kuba hasn't forgotten Monica
EINVAL means "Invalid argument "Krishna Kanth Yenumula

3 Answers

2
votes

I got this code from this link : Reboot with and without glibc

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#ifdef NO_GLIBC
#include <linux/reboot.h>
#else
#include <sys/reboot.h>
#endif

#ifdef NO_GLIBC
#ifndef LINUX_REBOOT_MAGIC1
#define LINUX_REBOOT_MAGIC1 0xfee1dead
#endif

#ifndef LINUX_REBOOT_MAGIC2
#define LINUX_REBOOT_MAGIC2 0x28121969
#endif

#ifndef LINUX_REBOOT_MAGIC2A
#define LINUX_REBOOT_MAGIC2A 0x05121996
#endif

#ifndef LINUX_REBOOT_MAGIC2B
#define LINUX_REBOOT_MAGIC2B 0x16041998
#endif

#ifndef LINUX_REBOOT_MAGIC2C
#define LINUX_REBOOT_MAGIC2C 0x20112000
#endif
#endif /* NO_GLIBC */

#ifndef LINUX_REBOOT_CMD_RESTART
#define LINUX_REBOOT_CMD_RESTART 0x1234567
#endif

#ifndef LINUX_REBOOT_CMD_HALT
#define LINUX_REBOOT_CMD_HALT 0xcdef0123
#endif

#ifndef LINUX_REBOOT_CMD_POWER_OFF
#define LINUX_REBOOT_CMD_POWER_OFF 0x4321fedc
#endif

#ifndef LINUX_REBOOT_CMD_RESTART2
#define LINUX_REBOOT_CMD_RESTART2 0xa1b3c3d4
#endif

#ifndef LINUX_REBOOT_CMD_CAD_ON
#define LINUX_REBOOT_CMD_CAD_ON 0x89abcdef
#endif

#ifndef LINUX_REBOOT_CMD_CAD_OFF
#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000
#endif

static void do_reboot(int command);

int
main(int argc, char *argv[]) {
  int reboot_command;

  if(argc == 1) {
    fprintf(stderr, "No command given.\n");
    fprintf(stderr, "Commands: RESTART, HALT, POWER_OFF, CAD_ON,
CAD_OFF.\n");
    return(2);
  } else if(strcasecmp(argv[1], "RESTART") == 0) {
    reboot_command = LINUX_REBOOT_CMD_RESTART;
  } else if(strcasecmp(argv[1], "HALT") == 0) {
    reboot_command = LINUX_REBOOT_CMD_HALT;
  } else if(strcasecmp(argv[1], "POWER_OFF") == 0) {
    reboot_command = LINUX_REBOOT_CMD_POWER_OFF;
  } else if(strcasecmp(argv[1], "RESTART2") == 0) {
    fprintf(stderr, "RESTART2 not supported. Try RESTART.\n");
    return(1);
  } else if(strcasecmp(argv[1], "CAD_ON") == 0) {
    reboot_command = LINUX_REBOOT_CMD_CAD_ON;
  } else if(strcasecmp(argv[1], "CAD_OFF") == 0) {
    reboot_command = LINUX_REBOOT_CMD_CAD_OFF;
  } else {
    fprintf(stderr, "%s not supported.\n", argv[1]);
    fprintf(stderr, "Commands: RESTART, HALT, POWER_OFF, CAD_ON,
CAD_OFF.\n");
    return(2);
  }

  do_reboot(reboot_command);

  // Not reached, unless command was CAD_ON or CAD_OFF.
  return(0);
}

static void
do_reboot(int cmd) {
  int reboot_status = 0;

  // Flush filesystem buffers before rebooting.
  sync();

  #ifdef NO_GLIBC
  // Old libc.
  reboot_status = reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd,
NULL);
  #else
  // glibc uses a wrapper around the system call.
  reboot_status = reboot(cmd);
  #endif

  if(reboot_status == -1) {
    if(errno == EPERM) {
      fprintf(stderr, "Permission denied.  Are you root?\n");
    }
  }
}

From this code, we can understand that the format for reboot in oldlibc is :
int reboot(int magic, int magic2, unsigned int cmd, void *arg);
and the format for reboot in glibc is : int reboot(int cmd);

Reason for Error 22: This error implies "Invalid argument" (EINVAL). glibc expects single argument in reboot(int cmd) function call ( glibc uses a wrapper around the system call), but we are giving multiple arguments. That's the reason for the error.

Please see the function call in the above code :

#ifdef NO_GLIBC
  // Old libc.
  reboot_status = reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd,
NULL);
  #else
  // glibc uses a wrapper around the system call.
  reboot_status = reboot(cmd);
  #endif
0
votes

Although I was unable to make the int reboot(int magic, int magic2, int cmd, void *arg); syscall API work, I was eventually able to do this syscall using the lower level, general syscall API, like so:

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>

int main(void) {
    syscall(169, 0xfee1dead, 672274793, 0x01234567);
    return 0;
}

Magic numbers can be interpreted using the man page at man reboot.2. And here is the equivalent x86_64 assembly:

section .text

global _start

_start:
  mov rax, 169
  mov rdi, 0fee1deadh
  mov rsi, 672274793
  mov rdx, 001234567h
  syscall

  ; The code below will never execute
  mov rax, 60
  mov rdi, 0
  syscall

My original goal was to execute this syscall from assembly, so although I'm still curious why the reboot function wouldn't work, I'm satisfied with what I've got.

-2
votes

So it looks like actual values are needed.

As it states on the below man page, you want to use the magic numbers as follows;

reboot( (int) 0xfee1dead, 672274793, 0x4321fedc ); // For Power Off as you've expressed above

https://man7.org/linux/man-pages/man2/reboot.2.html