5

I wrote a custom system call that compares two integers and returns the biggest one. Here's my kernel-side code:

max.c

#include <linux/kernel.h>
#include <linux/syscalls.h>

asmlinkage long sys_max(int num1, int num2) 
{
  if (num1 > num2) 
  {
    return num1;
  }

  else 
  {
    return num2;
  }
}

And here's my user-space code:

max.h

#include <unistd.h>
#define SYS_MAX 323

int max(int num1, int num2)
{
  int maxnumber = syscall(SYS_MAX, num1, num2);
  return maxnumber;
}

I'm using this little program to test the system call:

#include <stdio.h>
#include <max.h>

int main()
{
    int a, b;
    scanf("%d", &a);
    scanf("%d", &b);
    printf("%d", max(a, b));
    return 0;
}

It's working great for positive numbers, or when one is positive and the other negative, but max always returns -1 when dealing with two negative values. I've wondered if this is because of the int->long conversion, but I can't seem to understand what's causing the problem.

Bernardo Lopes
  • 171
  • 3
  • 19
  • `int max(int num1, int num2){}` - that's a function definition, not a declaration. Did you mean `int max(int, int);`? – melpomene Nov 07 '15 at 21:39
  • Does anything change if you rename your `max` function to `mymax`? – melpomene Nov 07 '15 at 21:40
  • @melpomene English is not my native language, so I'm not sure what's the right word here. But the point is that the function max must return an integer and receive two integers as parameters. – Bernardo Lopes Nov 07 '15 at 21:43
  • No, the point is that you need `;`, not `{}` in your header. – melpomene Nov 07 '15 at 21:44
  • @melpomene Changing the function name didn't change anything. – Bernardo Lopes Nov 07 '15 at 21:45
  • @melpomene The code is the one I wrote here, I was trying to say that max can't be changed to return a long. xD – Bernardo Lopes Nov 07 '15 at 21:46
  • No one said anything about `long`. And `{}` in your header is still wrong. – melpomene Nov 07 '15 at 21:48
  • @melpomene Just saying that I'm not even defining the function before declaring it. But I'll remove that line from the question since it's causing so much confusion. – Bernardo Lopes Nov 07 '15 at 21:49
  • SYS_MAX is the maximum number of functions available in syscall. It does not return the maximum of two numbers. – cup Nov 07 '15 at 22:23
  • @cup actually that's `__NR_syscalls` (which doesn't have a `SYS_` name to avoid name collisions). `SYS_MAX` is being defined by by OP in the question, correctly enough. – hobbs Nov 07 '15 at 22:38

1 Answers1

14

If a system call returns a negative value it's treated as an error, and special error handling code is invoked in libc. Namely, the return value is negated and moved into the errno global variable, and the return value of the system call becomes -1.

Actually, while reading about this I discovered that on Linux, only values from -4095 to -1 are treated as errors, but this is neither portable, nor helpful if you want your function to work on any possible values.

In short, you can't safely use the return value of a syscall to return a value that might be negative. The usual convention would be to pass a pointer to a destination variable to hold the result, and reserve the return code for success/failure. Note that when doing this with a syscall you will be working with a user-space pointer from kernel-space, so copy_to_user will be necessary to write the result.

hobbs
  • 223,387
  • 19
  • 210
  • 288
  • Thanks a lot for the effort and the answer. I'm having trouble implementing the copy_to_user and copy_from_user functions. Any tips on how to do that? – Bernardo Lopes Nov 07 '15 at 22:47
  • 1
    @BernardoLopes that's another question, and one that I can't answer well. Search for an answer, and if you can't figure it out, ask a new question on SO :) – hobbs Nov 07 '15 at 22:52
  • Thanks, I'll try with new code. I'll report back when it finishes compiling. – Bernardo Lopes Nov 07 '15 at 22:55