2
#include <stdio.h>

int main() {
  char *str = "11111111-22222222 r-xp 00000000 00:0e 1843624    /lib/libdl.so.0";
  unsigned long long start_addr, stop_addr, offset;
  char* access = NULL;
  char* filename = NULL;
  sscanf(str, "%llx-%llx %m[-rwxp] %llx %*[:0-9a-f] %*d %ms",
           &start_addr, &stop_addr, &access, &offset, &filename);

  printf("\n start : %x, stop : %x, offset : %x\n",start_addr,stop_addr,offset);
  printf("\n Permission : %s\n",access);
  printf("\n Filename : %s\n",filename);
    return 0;
}

On Linux this gives the correct output but on Solaris the file is called libdl.so (there is no libdl.so.0 on Solaris) so I wonder what makes this difference, there is not this file on Solaris and if I change to the filename of the Solaris installation (libdl.so) then it generates a segmentation fault.

$ cc Cperm.c ;./a.out 
Cperm.c: I funktion "main":
Cperm.c:11:3: varning: format "%x" förväntar sig argument av typen "unsigned int", men argument 2 har typen "long long unsigned int" [-Wformat]
Cperm.c:11:3: varning: format "%x" förväntar sig argument av typen "unsigned int", men argument 3 har typen "long long unsigned int" [-Wformat]
Cperm.c:11:3: varning: format "%x" förväntar sig argument av typen "unsigned int", men argument 4 har typen "long long unsigned int" [-Wformat]

 start : 11111111, stop : 22222222, offset : 0

 Permission : r-xp

 Filename : /lib/libdl.so.0

The above is on ubuntu and here is on Solaris where it compiles without warnings but generates a segmentation fault:

uname -a
SunOS 5.10 Generic_148888-03 sun4u sparc SUNW,Ultra-4
my:~>cc Cperm.c;./a.out 

 start : 0, stop : 11111111, offset : 0
Segmentation fault

Update

my:~>uname -a;gcc -Wall Cperm.c
SunOS 5.10 Generic_148888-03 sun4u sparc SUNW,Ultra-4
Cperm.c: In function `main':
Cperm.c:9: warning: unknown conversion type character `m' in format
Cperm.c:9: warning: long long unsigned int format, pointer arg (arg 5)
Cperm.c:9: warning: unknown conversion type character `m' in format
Cperm.c:9: warning: too many arguments for format
Cperm.c:11: warning: unsigned int format, different type arg (arg 2)
Cperm.c:11: warning: unsigned int format, different type arg (arg 3)
Cperm.c:11: warning: unsigned int format, different type arg (arg 4)
my:~>gcc Cperm.c
my:~>
Niklas Rosencrantz
  • 25,640
  • 75
  • 229
  • 424
  • 2
    Just FYI, setting LC_ALL=C (or en), or whatever the equivalent is on Solaris, will get you English messages... Which is helpful when pasting them to an English-language site. – derobert May 23 '13 at 19:50
  • The way to meddle with shared library's internals isn't the same... – vonbrand May 23 '13 at 21:46
  • Actually, it might have been better if he'd used locale `el_GR` or `ko_KR` as the warnings were red herrings. – msw May 24 '13 at 04:31
  • @Nick, regarding the Update, `gcc --version` would have been helpful as SunOS 5.10 was released in January 2005, which was in the GCC 3.x era. Nevertheless, `gcc -Wall -Werror` is pretty good at stopping silly printf/scanf bugs from getting through. – msw May 24 '13 at 06:30

3 Answers3

11

Check the man page for Solaris 10 sscanf. The %m modifier is not supported there.

You should also check the return value of sscanf.

Stephane Chazelas
  • 5,859
  • 2
  • 34
  • 31
4

Your compiler (on Ubuntu, and probably Solaris if you enabled the warning) told you what is wrong:

Cperm.c:11:3: varning: format "%x" förväntar sig argument av typen "unsigned int", men argument 2 har typen "long long unsigned int" [-Wformat]
⋮

You need to use %llx in your printf, just like you did in your sscanf.

Passing the wrong type of argument is undefined behavior. On Linux, it happened to work (this time); on Solaris, it didn't.

[You're really asking a question about the C language and libraries, you'd probably have better luck searching Stack Overflow for answers, rather than here.]

edit: see also msw's answer, which points out another problem, at least as important as this one.

Community
  • 1
  • 1
derobert
  • 49,731
  • 15
  • 94
  • 124
  • I also note the Solaris machine is reported as having a SPARC cpu. SPARC & x86 are different endianness, which ends up with treating a 64-bit value as a 32-bit number lucking out and picking the right half on one, the wrong half on the other. – alanc May 24 '13 at 01:35
  • @derobert - I almost didn't see the actual bug either, but this wasn't it. – msw May 24 '13 at 04:20
4

It's actually much simpler than it might look. Your code has never allocated space to store the string results into. This shorter code has the same defect:

#include <stdio.h>
int main() {
    char *word = NULL;
    sscanf("hello world", "%s", &word); 
    printf("%s\n", *word);
    return 0;
}

The reason it may "work" on one compiler but not another may have to do with how storage is allocated. Here is the error generated by that code:

cperm.c:5:5: error: format ‘%s’ expects argument of type ‘char *’, 
             but argument 3 has type ‘char **’

Which doesn't seem horrible, but it's actually fatal. Running gcc with the -Werror option would make that warning stop compilation and not create an a.out. Defining and using word properly

char word[64];
sscanf("hello world", "%63s", word); 
printf("%s\n", word);

compiles without error and works.

msw
  • 42,753
  • 9
  • 87
  • 112
  • Wow, yeah, I missed that! The code apparently has enough bugs to go around :-( – derobert May 24 '13 at 16:09
  • Thanks for bringing me back to this one as I saw the buffer overflow bug that I had in my sample code. `scanf` should be deprecated ;) – msw May 24 '13 at 16:16