1

I have a, rather philosophical, problem with verification of shmat function output. Doing this:

void * addr = shmat(shmid, NULL, 0);
if( addr == -1)
{
    perror("Uncool stuff");
}

gives me:

warning: comparison between pointer and integer

And this is perfectly ok. Unfortunately, manual says:

Upon success, shmat() returns the address where the segment is attached; 
otherwise, -1 is returned and errno is set to indicate the error.

This is quite standard behavior, but how should I handle it? Is if (addr == (void *) -1) correct? I've read this question and now I'm not really sure, if it would be 100% correct on every possible platform-blah-blah-blah ;-) I haven't found any other reliable sources on that matter.

I believe, that asking about signedness of pointers is a mistake, because pointers are not numbers. But in such case I'm not sure anymore...

Edit: this problem leads us to another question: is char *addr = (char*)shmat(...) correct? According to answers so far, it is, but if we consider no mmu, etc, etc... Yeah, maybe I'm losing touch with reality here, but I'm curious if there is THE proper way.

Community
  • 1
  • 1
Piotr Zierhoffer
  • 5,005
  • 1
  • 38
  • 59
  • Since this is `shmat()`, the only platform you care about is POSIX. Also, you are not asking for the signedness of a pointer (that would be `addr < 0`, you are comparing against the constant `(void *) -1`. – ninjalj Nov 30 '11 at 22:51
  • 2
    Note the following paragraph from the POSIX rationale for the similar `mmap()`: _Existing implementations of mmap() return the value -1 when unsuccessful. Since the casting of this value to type void * cannot be guaranteed by the ISO C standard to be distinct from a successful value, this volume of IEEE Std 1003.1-2001 defines the symbol MAP_FAILED, which a conforming implementation does not return as the result of a successful call._ – ninjalj Nov 30 '11 at 22:53
  • That is very interesting, but keeps things complicated :) I might look for a similar symbol for `shmat()`. Thanks! – Piotr Zierhoffer Nov 30 '11 at 23:01
  • there is no similar constant for `shmat`. Anyway, this tells that the only reason to not use `-1` directly is that it could conceivably be returned by a `mmap()` operation (think `MAP_FIXED` on a !MMU platform), which I doubt happens in any implementation. – ninjalj Nov 30 '11 at 23:07
  • Anyway, existing practice in POSIX systems is to use `(void *)-1` – ninjalj Nov 30 '11 at 23:07
  • `dlsym()` is another interface with a "strange" way to check for errors. I think these are the only 3 functions that return pointers that don't return `NULL` on error. – ninjalj Nov 30 '11 at 23:11
  • this shows you just one thing, don't use `shmat` and other oldish SysV interfaces if you can avoid it. Modern POSIX systems have `shm_open` and `mmap`. Have a look into the linux man overview: http://www.kernel.org/doc/man-pages/online/pages/man7/shm_overview.7.html – Jens Gustedt Nov 30 '11 at 23:31

3 Answers3

4

The documentation specifically says that shmat returns (void*)-1 on error, so that's what you should compare the result to. You might be looking at an older version of the documentation. It's a bit of a hack, but it should be ok on any POSIX-conforming systems (it makes assumptions that aren't guaranteed by the C standard). Converting the returned pointer to an integer type and then comparing to -1 may or may not work; consider, for example, that int and void* might have different sizes.

Don't try to check errno without first checking the returned value. In general, functions may set errno to some non-zero value even if they succeed. Always check the returned value to detect failure, then you can check errno to determine the kind of failure. And if you're going to check errno, be sure to set it to 0 before the call. (I'm not sure that's strictly necessary in this case, but it's good practice.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • That is possible, that my manual is not up to date - I have a Polish system and I've found the quote on the web, in both cases no mention about casting. Thanks! I think about @ninjajl comment to my question, I'd have to make some research on that. – Piotr Zierhoffer Nov 30 '11 at 22:58
  • The version of the man page in the [Linux man pages repository](https://github.com/mkerrisk/man-pages.git) has used `(void*)-1` since 2005-12-02; it undoubtedly took a while for the change to propagate. Disappointingly, IEEE Std 1003.1-2008 (POSIX) says "... Otherwise, the shared memory segment shall not be attached, *shmat()* shall return -1, and *errno* shall be set to indicate the error.". – Keith Thompson Nov 30 '11 at 23:44
1

yes, it's correct, but ugly.

For it to return -1 to you, it has to do the same kind of cast, so it should work on most platforms.

Keith Nicholas
  • 43,549
  • 15
  • 93
  • 156
1

IMO this test should work in nearly all cases if (addr == (void*)-1 ...

If you really want to sidestep the issue, do #include <errno.h> ... errno = 0; void * addr = shmat(shmid, NULL, 0); if (errno != 0) ... IOW use errno to detect the error rather than the return value from shmat.

(I would have a case in my unit tests for my assumptions; running them on a new platform would help you detect a bug caused by unexpected behavior on that platform.)

Art Swri
  • 2,799
  • 3
  • 25
  • 36
  • Checking errno is a nice solution, didn't think about it! It's clean and correct (if we forget about signals). Maybe it's not a 100% satisfying answer, but deftly +1 for it ;-) – Piotr Zierhoffer Nov 30 '11 at 22:41
  • 1
    Just checking `errno` is unsafe. Functions can set `errno` to a non-zero value even on success; for example, a function might call another function that fails, but the caller succeeds. – Keith Thompson Nov 30 '11 at 22:53
  • Well, yes, but shmat does not. I think, that it is incorrect only if we consider signal handling. – Piotr Zierhoffer Nov 30 '11 at 22:55
  • Well, to be honest, I can't really say if it does or not, can I? Manual would not mention such behavior... – Piotr Zierhoffer Nov 30 '11 at 22:59