4

I'm writing a linux kernel module that makes use of the exported symbol open_exec

struct file *open_exec(const char *name)

It returns a pointer, and I can check for an error with the IS_ERR macro:

if (IS_ERR(file))
    return file;

During compile time, I get this warning:

warning: return makes integer from pointer without a cast

This is because my function here returns an integer. If I try to cast it:

return (int) file;

I don't get a warning on my 32bit machine, but I do on my 64bit machine:

warning: cast from pointer to integer of different size

This is because the sizeof of an int and a pointer are the same on 32bit, but they differ on a 64bit machine.

Casting it or not, the code appears to work. I'd just like to get rid of the warning.

How do I properly cast a pointer to an integer and get the value I expect, while not getting a compiler warning? The value I expect is essentially an integer listed in include/asm-generic/errno-base.h of the linux kernel code base.

Since I'm only looking at the pointer as if it was an integer in the case where IS_ERR() is true, I can be sure that it does in-fact only hold an integer value.

Corey Henderson
  • 7,239
  • 1
  • 39
  • 43
  • the warning makes sense: if int can't hold a pointer, your code could get troubles; likely it won't, but you can't be sure; if your function must return a pointer, why are you using an int as return type? – ShinTakezou Jul 26 '11 at 05:31
  • because 95% of the cases where I return an error I know what the error is, and specify it (ie; return -EPERM;). In the case of a function that can error out, that returns a pointer, I want to extract what the error was, and report that. – Corey Henderson Jul 26 '11 at 05:37
  • 1
    I can't get it; "extract" the error before return, map it to an int, and return that int... why not? Or, report the error inside the func producing it; anyway, do not mix ints and pointers, even on 32bit is not good (how can you be sure the address does not match -EPERM by chance?) – ShinTakezou Jul 26 '11 at 05:42
  • 1
    taking a look at sources, use a long instead of an int could "solve" the problem without creating others? – ShinTakezou Jul 26 '11 at 05:47
  • "your code could get troubles; likely it won't" -- no, that's not likely at all, in fact just the opposite. – Jim Balter Jul 26 '11 at 06:48
  • @Jim Balter you mean, "it will get troubles" or "it won't get troubles at all"? Though I think it is not "best practice", in this specific case it seems it is not unreasonable (it is used); uintptr_t or similar could be better – ShinTakezou Jul 26 '11 at 07:00
  • @ShinTakezou When you say "likely it won't" and I say "no, that's not likely at all", I must mean that it's likely it will ... casting pointers to smaller types is a really bad idea. It might be that, if `IS_ERR(x)` is true, `x` must fit in an `int`, but I would never ever count on such a thing. – Jim Balter Jul 26 '11 at 07:36
  • 1
    @Jim ok, sorry, my english gets a bit confused with a negation over a probability :) – ShinTakezou Jul 26 '11 at 09:01

3 Answers3

8

The PTR_ERR() macro in linux/err.h, which is where IS_ERR() is also defined, converts a pointer that's really an error code into the appropriate type (a long).

You should use something like:

if (IS_ERR(file))
    return PTR_ERR(file);

Search for existing uses of PTR_ERR() in the source and you'll see this is a common pattern.

It might be appropriate for your function to return a long rather than an int - but all error codes should be representable in an int.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • Looks like that does the job, and I don't even need to cast it. Thanks! – Corey Henderson Jul 26 '11 at 06:40
  • Yes. It's sad how wrongheaded the other answers are ... never thinking of checking the API or realizing that this must be a common pattern. – Jim Balter Jul 26 '11 at 06:53
  • 2
    @Jim: to be honest, I misunderstood what was really being asked at first. Without the context of what happens to the `file` pointer in a non-error situation, it's easy to jump to the conclusion that the pointer is being returned as an `int` in the normal case. And then you get distracted from what the question was really asking. – Michael Burr Jul 26 '11 at 07:08
1

You can't properly cast a pointer to a type of smaller size, period. You could do some conversion if you were sure of what that pointer stored.

For example, if you know that a pointer has only lowest 32 bits set you can just cast it and use some compiler-specific pragma to suppress the warning. Or if you want to hash the pointer for using in something like a hash table you could xor the upper 32 bits with the lower 32 bits.

This can't be decided without more knowledge of how that int is used later.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • I'm going to go out on a limb here and say that I'm completely sure the pointer, in the case of IS_ERR() being true, contains one of the values in errno-base.h – Corey Henderson Jul 26 '11 at 05:30
  • an example of doing that would be just great :-D – Corey Henderson Jul 26 '11 at 05:36
  • @Corey Henderson: Suppose you're sure that `int` is wide enough. Then `(int)(intptr_t)value` would be the way to do that I suppose. If the warning persists look for how to suppress it. – sharptooth Jul 26 '11 at 05:37
  • @Corey Henderson: here's how suppression is done in Visual C++ so that you have and idea http://stackoverflow.com/q/4193476 – sharptooth Jul 26 '11 at 05:39
0

Im not sure I get how you sometimes want to return an number from errno-base.h and sometimes a pointer -- how would the receiving function be able to tell the two apart? That being equal, then on Linux GCC,

  • int is 32bit wide irrespective of whether you are on 32 or 64bit linux
  • pointers are 64 bit wide on 64 bit architectures, and 32 bite wide on 32 bit architectures
  • long are 32bit wide on 32bit architectures and 64 bit wide on 64 bit architectures.
  • long long are always 64bit wide

hence on a 64bit architecture casting a pointer to an int means that you will case a 64bit value to a 32bit value, and you can be somewhat sure that you will lose part of the 64bit information from the pointer -- and this is what the compiler warning is all about, as you point out yourself.

If you want to cast from pointer to something 'anonymous' then your choices should be either long, long long or void* -- with the void* being the most portable.

The other alternative is to record it as an offset, that is if you have a large memory area where you want to 'cast' to a 32bit integer, then convert it to something like;

  static struct mybigbuffer *globalbuffer;
   int cast2int(void*x)
   {
       return (int)(globalbuffer-(struct mybigbuffer*)x);
   }

however that is only work assuming that you know that your your memory will never exceed 2^31 records of globalbuf and that your pointers are assured to align on boundaries etc -- so unless you are 100% sure you know what you are doing, I would not recommended this either -- stick with the long or void* as the safe options.

Soren
  • 14,402
  • 4
  • 41
  • 67
  • I don't know for the types that are provided in kernel context, but generally in C there is `[u]intpr_t` for pointer to integer conversion. – Jens Gustedt Jul 26 '11 at 06:22