How to find which function from program reads from stdin? - c

I am currently working on fuzzing a program, and the code base is huge. To improve the performance, I am using persistent mode by creating a loop around the necessary function or code that reads from stdin. Right now using gdb, I am able to enumerate all the functions being used by the program like this:
set logging on
set confirm off
rbreak ^[^#]*$
run the binary
continue
This gives me all the functions that the program uses, but I think an easier way than reading hundreds of lines is by finding the function that reads from stdin. How would I be able to find the function that reads from stdin?

Since you're running Linux, virtually every function that reads from a stream (such as stdin) will ultimately do a read system call. (Less often, they will call readv.)
The C prototype for the read function is
ssize_t read(int fd, void *buf, size_t count);
and like most Linux system calls, this is pretty much the prototype for the actual system call (all the integer and pointer types are put into registers.)
On x86_64, the first argument to a system call will be in register rdi. (See Calling conventions.) A value of 0 means stdin.
So first we will tell GDB to stop the process upon entering the read system call, adding a condition to stop only when its first argument is 0:
(gdb) catch syscall read
Catchpoint 1 (syscall 'read' [0])
(gdb) condition 1 $rdi == 0
(gdb) run
Starting program: cat
Catchpoint 1 (call to syscall read), 0x00007fffff13b910 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
84 ../sysdeps/unix/syscall-template.S: No such file or directory.
Now do a backtrace to see all the functions in the call stack:
(gdb) bt
#0 0x00007fffff13b910 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
#1 0x00007fffff0d2a84 in __GI__IO_file_xsgetn (fp=0x7fffff3f98c0 <_IO_2_1_stdin_>, data=<optimized out>, n=4096)
at fileops.c:1442
#2 0x00007fffff0c7ad9 in __GI__IO_fread (buf=<optimized out>, size=1, count=4096, fp=0x7fffff3f98c0 <_IO_2_1_stdin_>)
at iofread.c:38
#3 0x00000000080007c2 in copy () at cat.c:6
#4 0x00000000080007de in main () at cat.c:12
(gdb) fr 3
#3 0x00000000080007c2 in copy () at cat.c:6
6 while ((n=fread(buf, 1, sizeof buf, stdin)) > 0)
(gdb) fr 4
#4 0x00000000080007de in main () at cat.c:12
12 copy();

How would I be able to find the function that reads from stdin?
In general, your question is equivalent to the halting problem. Consider this function:
ssize_t foo(int fd, void *buf, size_t count) { return read(fd, buf, count); }
Does this function read from stdin? It may or may not (depending on inputs), and therein lies the problem.
P.S. Your method of enumerating all functions that are called is exceedingly inefficient. You should look into building your program with -finstrument-functions instead. Example.

Related

malloc() and memset() behavior

I wrote some code to see how malloc() and memset() behave, and I found a case where I don't know what's going on.
I used malloc() to allocate 15 bytes of memory for a character array, and I
wanted to see what would happen if I used memset() incorrectly to set 100 bytes of memory in the pointer I created. I expected to see that memset() had set 15 bytes (and possibly trash some other memory). What I'm seeing when I run the program is that it's setting 26 bytes of memory to the character that I coded.
Any idea why there are 26 bytes allocated for the pointer I created? I'm compiling with gcc and glibc. Here's the code:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#define ARRLEN 14
int main(void) {
/* + 1 for the null terminator */
char *charptr = malloc((sizeof(*charptr) * ARRLEN) + 1);
if (!charptr)
exit(EXIT_FAILURE);
memset(charptr, '\0', (sizeof(*charptr) * ARRLEN) + 1);
/* here's the intentionally incorrect call to memset() */
memset(charptr, 'a', 100);
printf("sizeof(char) ------ %ld\n", sizeof(char));
printf("sizeof(charptr) --- %ld\n", sizeof(charptr));
printf("sizeof(*charptr) --- %ld\n", sizeof(*charptr));
printf("sizeof(&charptr) --- %ld\n", sizeof(&charptr));
printf("strlen(charptr) --- %ld\n", strlen(charptr));
printf("charptr string ---- >>%s<<\n", charptr);
free(charptr);
return 0;
}
This is the output I get:
sizeof(char) ------ 1
sizeof(charptr) --- 8
sizeof(*charptr) --- 1
sizeof(&charptr) --- 8
strlen(charptr) --- 26
charptr string ---- >>aaaaaaaaaaaaaaaaaaaaaaaa<<
First of all, this is undefined behavior, so anything can happen; as said in a comment, on my machine I get your exact same behavior with optimizations disabled, but turning on optimizations I get a warning about a potential buffer overflow at compile time (impressive job gcc!) and a big crash at runtime. Even better, if I print it with a puts before the printf calls I get it printed with a different number of a.
Still, I have the dubious luck to have the exact same behavior as you, so let's investigate. I compiled your program with no optimization and debug information
[matteo#teokubuntu ~/scratch]$ gcc -g memset_test.c
then I fired up the debugger and added a breakpoint on the first printf, just after the memset.
Reading symbols from a.out...done.
(gdb) break 20
Breakpoint 1 at 0x87e: file memset_test.c, line 20.
(gdb) r
Starting program: /home/matteo/scratch/a.out
Breakpoint 1, main () at memset_test.c:20
20 printf("sizeof(char) ------ %ld\n", sizeof(char));
now we can set a hardware write breakpoint on the 26th memory location pointed by charptr
(gdb) p charptr
$1 = 0x555555756260 'a' <repeats 100 times>
(gdb) watch charptr[26]
Hardware watchpoint 2: charptr[26]
... and so...
(gdb) c
Continuing.
Hardware watchpoint 2: charptr[26]
Old value = 97 'a'
New value = 0 '\000'
_int_malloc (av=av#entry=0x7ffff7dcfc40 <main_arena>, bytes=bytes#entry=1024) at malloc.c:4100
4100 malloc.c: File o directory non esistente.
(gdb) bt
#0 _int_malloc (av=av#entry=0x7ffff7dcfc40 <main_arena>, bytes=bytes#entry=1024) at malloc.c:4100
#1 0x00007ffff7a7b0fc in __GI___libc_malloc (bytes=1024) at malloc.c:3057
#2 0x00007ffff7a6218c in __GI__IO_file_doallocate (fp=0x7ffff7dd0760 <_IO_2_1_stdout_>) at filedoalloc.c:101
#3 0x00007ffff7a72379 in __GI__IO_doallocbuf (fp=fp#entry=0x7ffff7dd0760 <_IO_2_1_stdout_>) at genops.c:365
#4 0x00007ffff7a71498 in _IO_new_file_overflow (f=0x7ffff7dd0760 <_IO_2_1_stdout_>, ch=-1) at fileops.c:759
#5 0x00007ffff7a6f9ed in _IO_new_file_xsputn (f=0x7ffff7dd0760 <_IO_2_1_stdout_>, data=<optimized out>, n=23)
at fileops.c:1266
#6 0x00007ffff7a3f534 in _IO_vfprintf_internal (s=0x7ffff7dd0760 <_IO_2_1_stdout_>,
format=0x5555555549c8 "sizeof(char) ------ %ld\n", ap=ap#entry=0x7fffffffe330) at vfprintf.c:1328
#7 0x00007ffff7a48f26 in __printf (format=<optimized out>) at printf.c:33
#8 0x0000555555554894 in main () at memset_test.c:20
(gdb)
So, it's just malloc code invoked (more or less indirectly) by printf doing its stuff on the memory block immediately adjacent to the one it gave you (possibly marking it as used).
Long story short: you took memory that wasn't yours and it's now getting modified by its rightful owner at the first occasion when he needed it; nothing particularly strange or interesting.
I used malloc() to allocate 15 bytes of memory for a character array,
and I wanted to see what would happen if I used memset() incorrectly
to set 100 bytes of memory in the pointer I created.
What happens is undefined behavior as far as the language standard is concerned, and whatever actually does happen in a particular case is not predictable from the source code and may not be consistent across different C implementations or even different runs of the same program.
I expected to see
that memset() had set 15 bytes (and possibly trash some other memory).
That would be one plausible result, but that you had any particular expectation at all is dangerous. Do not assume that you can predict what manifestation UB will take, not even based on past experience. And since you should not do that, and cannot learn anything useful from that way, it is not worthwhile to experiment with UB.
What I'm seeing when I run the program is that it's setting 26 bytes
of memory to the character that I coded.
Any idea why there are 26 bytes allocated for the pointer I created?
Who says your experiment demonstrates that to be the case? Not only the memset() but also the last printf() exhibits UB. The output tells you nothing other than what the output was. That time.
Now in general, malloc may reserve a larger block than you request. Many implementations internally manage memory in chunks larger than one byte, such as 16 or 32 bytes. But that has nothing to do one way or another with the definedness of your program's behavior, and it does not speak to your output.

How to send arbitary bytes to STDIN of a program in gdb?

I am developing buffer overflow exercises for students. In this context you often have to provide arbitary bytes as input for programs (return addresses).
Assume this example:
#import <stdio.h>
#import <string.h>
void func() {
char buf[4];
gets(buf);
}
int main (int argc, char** argv) {
func();
return 0;
}
Normally I experiment with gdb, until I found a solution, which can then be formulated like
python -c 'print "A"*8+"\x08\x04\88\72"' | ./program
While developing more and more complex exercises, the difficulty to find a solution increases. Sometimes overwriting the return address in gdb via
set {int}address_of_address = new_address
works, but the python-approach does not. It would be nice to debug this and to be able to enter bytes like "\x04" in gdb, while the program is running, analyzing the effects.
Is there any way to do this?
This question seems related but is answered with the python-approach: Sending arbitrary bytes to fgets from stdin
Mine goes beyond that :-/
It would be nice to debug this and to be able to enter bytes like
"\x04" in gdb, while the program is running, analyzing the effects
To do this you need 2 consoles: the first one to enter bytes in program stdin, the second one for gdb debug session.
You can first run program in 1st console until it stops waiting for bytes from stdin. Then run gdb in 2nd console and attach to a program by it's pid. You will be able to debug and enter bytes simultaneously from 2 different consoles.
"while the program is running" is one part of the problem. The other one is being able to set breakpoints beforehand, to "analyze the effects".
GDB's default behaviour is to run the program as a child process, thus using the same standard streams. So it is impossible to write to the child's stdin while being in GDB's CLI because, at this moment, it is being read by GDB, not your program.
The simplest solution, avoiding tty workarounds (tty command + stty setups + reading/writing to /proc/<pid>/fd/{0,1}), is to make your code testable and "callable" from GDB. You'll then be able to pass your string arguments to your functions in order to test and debug them.
For example:
#include <stdio.h>
#include <unistd.h>
void exploitme(char* str)
{
printf(str);
}
int main()
{
while (1)
{
char str[10];
fgets(str, sizeof (str), stdin);
exploitme(str);
}
return 0;
}
exploitme() is the exploit case correctly wrapped in a single entry point so that it is now possible to call it once everything it uses is correctly initialized. You can then call it using command call once main() breakpoint is reached (so that the C runtime inits, performed in main's caller, are done).
~/test $ gdb ./a.out
(gdb) call exploitme("hello")
You can't do that without a process to debug.
(gdb) b main
Breakpoint 1 at 0x4005ae: file helloworld.c, line 14.
(gdb) r
Starting program: /home/julio/test/a.out
Breakpoint 1, main () at helloworld.c:14
14 fgets(str, sizeof (str), stdin);
(gdb) call exploitme("hello")
(gdb) call exploitme("hello\n")
hellohello
(gdb) call exploitme("AAAAAAAA\x08\x04\88\72\n")
AAAAAAA�:
(gdb) b exploitme
Breakpoint 2 at 0x400592: file helloworld.c, line 6.
(gdb) call exploitme("foo")
Breakpoint 2, exploitme (str=0x602010 "foo") at helloworld.c:6
6 printf(str);
The program being debugged stopped while in a function called from GDB.
Evaluation of the expression containing the function
(exploitme) will be abandoned.
When the function is done executing, GDB will silently stop.
Note that you benefit from GDB's argument expansion which includes the C string evaluation.
The other (longer and more complex) solution, as explained, is to run your program under another tty, so that you can independently write to GDB and your program.

Random seeding seg fault

I am asked to put a random seeding into a program that is called when a user types in -r. It is shown to look like
-r1234567
So as a global variable I set Random = 1234567.
I added this to the top of my function as well:
printf("Seed: %d\n", Random);
srand48(Random);
Every time I use -r1234567 My program segfaults and says this:
Program received signal SIGSEGV, Segmentation fault.
____strtol_l_internal (nptr=0x0, endptr=0x0, base=10, group=<optimized out>,
loc=0x7ffff7ad8020) at ../stdlib/strtol_l.c:298
298 ../stdlib/strtol_l.c: No such file or directory.
(gdb) bt
#0 ____strtol_l_internal (nptr=0x0, endptr=0x0, base=10, group=<optimized out>,
loc=0x7ffff7ad8020) at ../stdlib/strtol_l.c:298
#1 0x00007ffff77589e0 in atoi (nptr=<optimized out>) at atoi.c:28
#2 0x0000000000401d8c in getCommandLine (argc=6, argv=0x7fffffffe238)
at prog.c:171
#3 0x0000000000401514 in main (argc=6, argv=0x7fffffffe238) at prog.c:35
(gdb) up
#1 0x00007ffff77589e0 in atoi (nptr=<optimized out>) at atoi.c:28
28 atoi.c: No such file or directory.
(gdb) up
#2 0x0000000000401d8c in getCommandLine (argc=6, argv=0x7fffffffe238)
at prog.c:171
warning: Source file is more recent than executable.
In my command like: the case looks like this:
while ((c = getopt(argc, argv, "g:n:a:h:s:d:v:r")) != -1)
case 'r': Random = atoi(optarg); break;
So a user wants to to randomize using a seed. They do ./program -r1234567. This then goes into the two lines of code that I put above and should randomize.
Any suggestions?
Your problem is the getopt call, in that you forgot to tell getopt that the -r argument takes a value:
getopt(argc, argv, "g:n:a:h:s:d:v:r:")
/* ^ */
/* | */
/* Add colon here */
Because of that, the optarg pointer is NULL when you call atoi.
you are giving -r1234567 from the command line , How you are parsing it and giving ito atoi ?
if atoi get "1234567" as its argument then there should not be any problem .. Can you paste the logic which parses the argv[1] ?
Are declaring Random as an int?. srand48 takes a long as an argument so you should be usuing atol instead of atoi:
long Random = 42;
printf("Seed: %d\n", Random);
srand48(Random);
The problem is .. you are missing ":" at the end of argument r ...because of this NULL is being passed to atoi .. try adding ":" at the end of r in the getopt !!

New segmentation fault with previously working C code

this code is meant to take a list of names in a text file, and convert to email form
so Kate Jones becomes kate.jones#yahoo.com
this code worked fine on linux mint 12, but now the exact same code is giving a segfault on arch linux.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *fp;
fp = fopen("original.txt", "r+");
if (fp == NULL )
{
printf("error opening file 1");
return (1);
}
char line[100];
char mod[30] = "#yahoo,com\n";
while (fgets(line, 100, fp) != NULL )
{
int i;
for (i = 0; i < 100; ++i)
{
if (line[i] == ' ')
{
line[i] = '.';
}
if (line[i] == '\n')
{
line[i] = '\0';
}
}
strcat(line, mod);
FILE *fp2;
fp2 = fopen("final.txt", "a");
if (fp == NULL )
{
printf("error opening file 2");
return (1);
}
if (fp2 != NULL )
{
fputs(line, fp2);
fclose(fp2);
}
}
fclose(fp);
return 0;
}
Arch Linux is a fairly fresh install, could it be that there is something else I didn't install that C will need?
I think the problem would be when your original string plus mod exceeds 100 characters.
When you call strcat, it simply copies the string from the second appended to the first, assuming there is enough room in the first string which clearly doesn't seem to be the case here.
Just increase the size of line i.e. it could be
char line[130]; // 130 might be more than what is required since mod is shorter
Also it is much better to use strncat
where you can limit maximum number of elements copied to dst, otherwise, strcat can still go beyond size without complaining if given large enough strings.
Though a word of caution with strncat is that it does not terminate strings with null i.e. \0 on its own, specially when they are shorter than the given n. So its documentation should be thoroughly read before actual use.
Update: Platform specific note
Thought of adding, it is by sheer coincidence that it didn't seg fault on mint and crashed on arch. In practice it is invoking undefined behavior and should crash sooner or latter. There is nothing platform specific here.
Firstly your code isn't producing segmentation fault. Instead it will bring up "Stack Smashing" and throws below libc_message in the output console.
*** stack smashing detected ***: _executable-name-with-path_ terminated.
Stack buffer overflow bugs are caused when a program writes more data to a buffer located on the stack than there was actually allocated for that buffer.
Stack Smashing Protector (SSP) is a GCC extension for protecting applications from such stack-smashing attacks.
And, as said in other answers, your problem gets resolved with incrementing (strcat() function's first argument). from
char line[100]
to
char line[130]; // size of line must be atleast `strlen(line) + strlen(mod) + 1`. Though 130 is not perfect, it is safer
Lets see where the issue exactly hits in your code:
For that I am bringing up disassembly code of your main.
(gdb) disas main
Dump of assembler code for function main:
0x0804857c <+0>: push %ebp
0x0804857d <+1>: mov %esp,%ebp
0x0804857f <+3>: and $0xfffffff0,%esp
0x08048582 <+6>: sub $0xb0,%esp
0x08048588 <+12>: mov %gs:0x14,%eax
0x0804858e <+18>: mov %eax,0xac(%esp)
..... //Leaving out Code after 0x0804858e till 0x08048671
0x08048671 <+245>: call 0x8048430 <strcat#plt>
0x08048676 <+250>: movl $0x80487d5,0x4(%esp)
.... //Leaving out Code after 0x08048676 till 0x08048704
0x08048704 <+392>: mov 0xac(%esp),%edx
0x0804870b <+399>: xor %gs:0x14,%edx
0x08048712 <+406>: je 0x8048719 <main+413>
0x08048714 <+408>: call 0x8048420 <__stack_chk_fail#plt>
0x08048719 <+413>: leave
0x0804871a <+414>: ret
Following the usual assembly language prologue,
Instruction at 0x08048582 : stack grows by b0(176 in decimal) bytes for allowing storage stack contents for the main function.
%gs:0x14 provides the random canary value used for stack protection.
Instruction at 0x08048588 : Stores above mentioned value into the eax register.
Instruction at 0x0804858e : eax content(canary value) is pushed to stack at $esp with offset 172
Keep a breakpoint(1) at 0x0804858e.
(gdb) break *0x0804858e
Breakpoint 1 at 0x804858e: file program_name.c, line 6.
Run the program:
(gdb) run
Starting program: /path-to-executable/executable-name
Breakpoint 1, 0x0804858e in main () at program_name.c:6
6 {
Once program pauses at the breakpoint(1), Retreive the random canary value by printing the contents of register 'eax'
(gdb) i r eax
eax 0xa3d24300 -1546501376
Keep a breakpoint(2) at 0x08048671 : Exactly before call strcat().
(gdb) break *0x08048671
Breakpoint 2 at 0x8048671: file program_name.c, line 33.
Continue the program execution to reach the breakpoint (2)
(gdb) continue
Continuing.
Breakpoint 2, 0x08048671 in main () at program_name.c:33
print out the second top stack content where we stored the random canary value by executing following command in gdb, to ensure it is the same before strcat() is called.
(gdb) p *(int*)($esp + 172)
$1 = -1546501376
Keep a breakpoint (3) at 0x08048676 : Immediately after returning from call strcat()
(gdb) break *0x08048676
Breakpoint 3 at 0x8048676: file program_name.c, line 36.
Continue the program execution to reach the breakpoint (3)
(gdb) continue
Continuing.
Breakpoint 3, main () at program_name.c:36
print out the second top stack content where we stored the random canary value by executing following command in gdb, to ensure it is not corrupted by calling strcat()
(gdb) p *(int*)($esp + 172)
$2 = 1869111673
But it is corrupted by calling strcat(). You can see $1 and $2 are not same.
Lets see what happens because of corrupting the random canary value.
Instruction at 0x08048704 : Pulls the corrupted random canary value and stores in 'edx` register
Instruction at 0x0804870b : xor the actual random canary value and the contents of 'edx' register
Instruction at 0x08048712 : If they are same, jumps directly to end of main and returns safely. In our case random canary value is corrupted and 'edx' register contents is not the same as the actual random canary value. Hence Jump condition fails and __stack_chk_fail is called which throws libc_message mentioned in the top of the answer and aborts the application.
Useful Links:
IBM SSP Page
Interesting Read on SSP - caution pdf.
Since you didn't tell us where it faults I'll just point out some suspect lines:
for(i=0; i<100; ++i)
What if a line is less than 100 chars? This will read uninitialized memory - its not a good idea to do this.
strcat(line, mod);
What if a line is 90 in length and then you're adding 30 more chars? Thats 20 out of bounds..
You need to calculate the length and dynamically allocate your strings with malloc, and ensure you don't read or write out of bounds, and that your strings are always NULL terminated. Or you could use C++/std::string to make things easier if it doesn't have to be C.
Instead of checking for \n only, for the end of line, add the check for \r character also.
if(line[i] == '\n' || line[i] == '\r')
Also, before using strcat ensure that line has has enough room for mod. You can do this by checking if (i < /* Some value far less than 100 */), if i == 100 then that means it never encountered a \n character hence \0 was not added to line, hence Invalid memory Access occurs inside strcat() and therefore Seg Fault.
Fixed it. I simply increased the size of my line string.

Heap corruption in HP-UX?

I'm trying to understand what's going wrong with a program run in HP-UX 11.11 that results in a SIGSEGV (11, segmentation fault):
(gdb) bt
#0 0x737390e8 in _sigfillset+0x618 () from /usr/lib/libc.2
#1 0x73736a8c in _sscanf+0x55c () from /usr/lib/libc.2
#2 0x7373c23c in malloc+0x18c () from /usr/lib/libc.2
#3 0x7379e3f8 in _findbuf+0x138 () from /usr/lib/libc.2
#4 0x7379c9f4 in _filbuf+0x34 () from /usr/lib/libc.2
#5 0x7379c604 in __fgets_unlocked+0x84 () from /usr/lib/libc.2
#6 0x7379c7fc in fgets+0xbc () from /usr/lib/libc.2
#7 0x7378ecec in __nsw_getoneconfig+0xf4 () from /usr/lib/libc.2
#8 0x7378f8b8 in __nsw_getconfig+0x150 () from /usr/lib/libc.2
#9 0x737903a8 in __thread_cond_init_default+0x100 () from /usr/lib/libc.2
#10 0x737909a0 in nss_search+0x80 () from /usr/lib/libc.2
#11 0x736e7320 in __gethostbyname_r+0x140 () from /usr/lib/libc.2
#12 0x736e74bc in gethostbyname+0x94 () from /usr/lib/libc.2
#13 0x11780 in dnetResolveName (name=0x400080d8 "smtp.org.com", hent=0x737f3334) at src/dnet.c:64
..
The problem seems to be occurring somewhere inside libc! A system call trace ends with:
Connecting to server smtp.org.com on port 25
write(1, "C o n n e c t i n g t o s e ".., 51) .......................... = 51
open("/etc/nsswitch.conf", O_RDONLY, 0666) ............................... [entry]
open("/etc/nsswitch.conf", O_RDONLY, 0666) ................................... = 5
Received signal 11, SIGSEGV, in user mode, [SIG_DFL], partial siginfo
Siginfo: si_code: I_NONEXIST, faulting address: 0x400118fc, si_errno: 0
PC: 0xc01980eb, instruction: 0x0d3f1280
exit(11) [implicit] ............................ WIFSIGNALED(SIGSEGV)|WCOREDUMP
Last instruction by the program:
struct hostent *him;
him = gethostbyname(name); // name == "smtp.org.com" as shown by gdb
Is this a problem with the system, or am I missing something?
Any guidance for digging deeper would be appreciated.
Thx.
Long story short: vsnprintf corrupted my heap under HP-UX 11.11.
vsnprintf was introduced in C99 (ISO/IEC 9899:1999) and "is equivalent to snprintf, with the variable argument list" (§7.19.6.12.2), snprintf (§7.19.6.5.2): "If n is zero, nothing is written".
Well, HP UX 11.11 doesn't comply with this specification. When 2nd arg == 0, arguments are written at the end of the 1st arg.. which, of course, corrupts the heap (I allocate no space when maxsize==0, given that nothing should be written).
HP manual pages are unclear ("It is the user's responsibility to ensure that enough storage is available."), nothing is said regarding the case of maxsize==0. Nice trap.. at the very least, the WARNINGS section of the man page should warn std-compliant users..
It's an egg/chicken pb: vnsprintf is variadic, so for the "user's responsibility" to ensure that enough storage is available" the "user's responsibility" must first know how much space is needed. And the best way to do that is to call vnsprintf with 2nd arg == 0: it should then return the amount of space required and sprintfs nothing.. well, except HP's !
One solution to use vnsprintf under this std violation to determine needed space: malloc 1 byte more to your buffer (1st arg) and call vnsprintf(buf+buf.length,1,..). This only puts a \0 in the new byte you allocated. Silly, but effective. If you're under wchar conditions, malloc(sizeof..).
Anyway, workaround is trivial : never call v/snprintf under HP-UX with maxsize==0!
I now have a happy stable program!
Thanks to all contributers.
Heap corruption through vsnprintf under HP-UX B11.11
This program prints "##" under Linux/Cygwin/..
It prints "#fooo#" under HP-UX B11.11:
#include <stdarg.h>
#include <stdio.h>
const int S=2;
void f (const char *fmt, ...) {
va_list ap;
int actualLen=0;
char buf[S];
bzero(buf, S);
va_start(ap, fmt);
actualLen = vsnprintf(buf, 0, fmt, ap);
va_end(ap);
printf("#%s#\n", buf);
}
int main () {
f("%s", "fooo");
return 0;
}
Whenever this situation happens to me (unexpected segfault in a system lib), it is usually because I did something foolish somewhere else, i.e. buffer overrun, double delete on a pointer, etc.
In those instances where my mistake is not obvious, I use valgrind. Something like the following is usually sufficient:
valgrind -v --leak-check=yes --show-reachable=yes ./myprog
I assume valgrind may be used in HP-UX...
Your stack trace is in malloc which almost certainly means that somewhere you corrupted one of malloc's data structures. As a previous answer said, you likely have a buffer overrun or underrun and corrupted one of the items allocated off the heap.
Another explanation is that you tried to do a free on something that didn't come from the heap, but that's less likely--that would probably have crashed right in free.
Reading the (OS X) manpage says that gethostbyname() returns a pointer, but as far as I can tell may not be allocating memory for that pointer. Do you need to malloc() first? Try this:
struct hostent *him = malloc(sizeof(struct hostent));
him = gethostbyname(name);
...
free(him);
Does that work any better?
EDIT: I tested this and it's probably wrong. Granted I used the bare string "stmp.org.com" instead of a variable, but both versions (with and without malloc()ing) worked on OS X. Maybe HP-UX is different.

Resources