Why segfaults occur with string.h functions? - c

With the same command in my coworker's PC, my program works without the problem.
But in my PC, the program crashes with segfault;
GDB backtrace at core reads as follows:
#0 strrchr () at ../sysdeps/x86_64/strrchr.S:32
32 ../sysdeps/x86_64/strrchr.S: no such file or directory
(gdb) bt
#0 strrchr () at ../sysdeps/x86_64/strrchr.S:32
#1 0x00007f10961236d7 in dirname (path=0x324a47a0 <error: Cannot access memory at address 0x324a47a0>) at dirname.c:31
I'm already compiling the executable with -g -ggdb options.
Odd thing is that.. with valgrind the program works without error in my PC as well.
How can I solve the problem? I've observed that the errors occur only with strrchr, strcmp, strlen, ... string.h functions.
+Edit: the gdb backtrace indicates that the program crashes here:
char* base_dir = dirname(get_abs_name(test_dir));
where get_abs_name is defined as
char* get_abs_name(char* dir) {
char abs_path[PATH_MAX];
char* c = malloc(PATH_MAX*sizeof(char));
realpath(dir, abs_path);
strcpy(c, abs_path);
return c;
}
+Edit2: 'dir' is a path of certain file, like '../program/blabla.jpg'.
Using valgrind,
printf("%s\n", dir)
normally prints '/home/frozenca/path_to_program'.
I can't guess why the program crashes without valgrind..

We cannot know for sure without a Minimal, Complete, and Verifiable example. Your code looks mostly correct (albeit convoluted), except you do not check for errors.
char* get_abs_name(char* dir) {
char abs_path[PATH_MAX];
char* c = malloc(PATH_MAX*sizeof(char)); /* this may return NULL */
realpath(dir, abs_path); /* this may return NULL */
strcpy(c, abs_path);
return c;
}
Now, how could this lead to an error like you see? Well, if malloc returns NULL, you'll get a crash right away in strcpy. But if realpath fails:
The content of abs_path remains undefined.
So strcpy(c, abs_path) will copy undefined content. Which could lead to it copying just one byte if abs_path[0] happens to be \0. But could also lead to massive heap corruption. Which happens depends on unrelated conditions, such as how the program is compiled, and whether some debugging tool such as valgrind is attached.
TL;DR: get into the habit of checking every function that may fail.
char* get_abs_name(char* dir) {
char abs_path[PATH_MAX];
char* c = malloc(PATH_MAX*sizeof(char));
if (!c) { return NULL; }
if (!realpath(dir, abs_path)) {
free(c);
return NULL;
}
strcpy(c, abs_path);
return c;
}
Or, here, you can simplify it alot assuming a GNU system or POSIX.1-2008 system:
char * get_abs_name(const char * dir) {
return realpath(dir, NULL);
}
Note however that either way, in your main program, you also must check that get_abs_name() did not return NULL, otherwise dirname() will crash.

Drop your function entirely and use the return value of realpath(dir, NULL) instead.

Convert type
char* c = malloc(PATH_MAX*sizeof(char));
Thanks!

Related

Malloc only works with valgrind on. How to debug?

to put you in some context here, project i test with valgrind is slightly modified version of distcc. The function that fails has not been changed. The exact place in code that is problematic is function dcc_compress_file_lzo1x in compress.c. It goes like this:
int dcc_compress_file_lzo1x(int in_fd,
size_t in_len,
char **out_buf,
size_t *out_len)
{
char *in_buf = NULL;
int ret;
if ((in_buf = malloc(in_len)) == NULL) {
rs_log_error("allocation of %ld byte buffer failed",
(long) in_len);
ret = EXIT_OUT_OF_MEMORY;
goto out;
}
The problem here is malloc in if statement. If I run this program normally it fails randomly(I wrapped whole thing in debug prints), by which i mean that program crashes while doing malloc, without producing error print. On the other hand if i start program with valgrind, everything passes and valgrind doesn't produce anything useful.
I'm not looking for easy answer. I would just like to know how to even debug this, because I'm out of ideas.

C - Different array behavior linux/windows?

I have a litle probem with C programming. I have to write a programm for the university and I wrote the whole programm on my windows pc.
I tried it afterwards on a linux system and I got different output.
Here is the code:
char *compress(char *input,int lineSize)
{
int n = 0;
char *compr = (char*)malloc(lineSize * sizeof(char));
//compr++;
char c = *input;
while(*input != '\0')
{
printf("compr: %s\n",compr);
if(*input != c)
{
snprintf(compr,lineSize,"%s%c%d",compr,c,n);
n = 0;
c = *input;
n++;
}
else
{
n++;
}
input++;
}
snprintf(compr,lineSize,"%s%c%d%c",compr,c,n,'\0');
printf("compr: %s\n",compr);
return compr;
}
this works as it should on my windows pc, but when I run it on linux I get a file writing error + the "compr" array is empty :/
I hope anyone could help me, couldnt find any solution.
thanks
Compile with warnings:
warning: function returns address of local variable [enabled by default]
Another possible problem:
snprintf(compr,lineSize,"%s%c%d%c",compr,c,n,'\0');
From the sprintf man page:
Some programs imprudently rely on code such as the following
sprintf(buf, "%s some further text", buf);
to append text to buf. However, the standards explicitly note that the
results are undefined if source and destination buffers overlap when
calling sprintf(), snprintf(), vsprintf(), and vsnprintf(). Depending
on the version of gcc(1) used, and the compiler options employed,
calls such as the above will not produce the expected results.

execvp call in git's source for external shell cmd returns EFAULT (Bad address) errno, seemingly only in 64 bit. Googling reveals nothing

UPDATE: running git diff with valgrind results in
Syscall param execve(argv) points to uninitialised byte(s)
And the output from strace is not fully decoded--i.e., there are hex numbers among the array of strings that is argv.
...
This started out like a superuser problem but it's definitely moved into SO's domain now.
But anyway, here is my original SU post detailing the problem before I looked at the source very much: https://superuser.com/questions/795751/various-methods-of-trying-to-set-up-a-git-diff-tool-lead-to-fatal-cannot-exec
Essentially, following standard procedure to set up vimdiff as a diff tool by setting the external directive under [diff] in .gitconfig leads to this errors like this:
fatal: cannot exec 'git_diff_wrapper': Bad address
external diff died, stopping at HEAD:switch-monitor.sh.
It happens on my Linux Mint 17 64 bit OS, as well as on an Ubuntu 14.04 64 bit OS on a virtualbox VM, but not in an Ubuntu 14.04 32 bit VM...
Googling reveals no similar problems. I've spent a lot of time looking at git's source to figure this out. Bad address is the description returned by strerror for an EFAULT error. Here is a short description of EFAULT from execve manpage:
EFAULT filename points outside your accessible address space
I've tracked down how the error message is pieced together by git, and have used that to narrow down the source of the problem quite a bit. Let's start here:
static int execv_shell_cmd(const char **argv)
{
const char **nargv = prepare_shell_cmd(argv);
trace_argv_printf(nargv, "trace: exec:");
sane_execvp(nargv[0], (char **)nargv);
free(nargv);
return -1;
}
This function should not return control, but it does due to the error. The actual execvp call is in sane_execvp, but perhaps prepare_shell_cmd is of interest, though I don't spot any problems:
static const char **prepare_shell_cmd(const char **argv)
{
int argc, nargc = 0;
const char **nargv;
for (argc = 0; argv[argc]; argc++)
; /* just counting */
/* +1 for NULL, +3 for "sh -c" plus extra $0 */
nargv = xmalloc(sizeof(*nargv) * (argc + 1 + 3));
if (argc < 1)
die("BUG: shell command is empty");
if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
#ifndef GIT_WINDOWS_NATIVE
nargv[nargc++] = SHELL_PATH;
#else
nargv[nargc++] = "sh";
#endif
nargv[nargc++] = "-c";
if (argc < 2)
nargv[nargc++] = argv[0];
else {
struct strbuf arg0 = STRBUF_INIT;
strbuf_addf(&arg0, "%s \"$#\"", argv[0]);
nargv[nargc++] = strbuf_detach(&arg0, NULL);
}
}
for (argc = 0; argv[argc]; argc++)
nargv[nargc++] = argv[argc];
nargv[nargc] = NULL;
return nargv;
}
It doesn't look like they messed up the terminating NULL pointer (the absence of which is known to cause EFAULT).
sane_execvp is pretty straightforward. It's a call to execvp and returns -1 if it fails.
I haven't quite figured out what trace_argv_printf does, though it looks like it might would affect nargv and maybe screw up the terminating NULL pointer? If you'd like me to include it in this post, let me know.
I have been unable to reproduce an EFAULT with execvp in my own C code thus far.
This is git 1.9.1, and the source code is available here: https://www.kernel.org/pub/software/scm/git/git-1.9.1.tar.gz
Any thoughts on how to move forward?
Thanks
Answer (copied from comments): it seems to be a bug in git 1.9.1. The (old) diff.c code, around line 2910-2930 or so, fills in an array of size 10 with arguments, before calling the run-command code. But in one case it puts in ten actual arguments and then an 11th NULL. Depending on the whims of the compiler, the NULL may get overwritten with some other local variable (or the NULL might overwrite something important).
Changing the array to size 11 should fix the problem. Or just update to a newer git (v2.0.0 or later); Jeff King replaced the hard-coded array with a dynamic one, in commits 82fbf269b9994d172719b2d456db5ef8453b323d and ae049c955c8858899467f6c5c0259c48a5294385.
Note: another possible cause for bad address is the use of run-command.c#exists_in_PATH() by run-command.c#sane_execvp().
This is fixed with Git 2.25.1 (Feb. 2020).
See commit 63ab08f (07 Jan 2020) by brian m. carlson (bk2204).
(Merged by Junio C Hamano -- gitster -- in commit 42096c7, 22 Jan 2020)
run-command: avoid undefined behavior in exists_in_PATH
Noticed-by: Miriam R.
Signed-off-by: brian m. carlson
In this function, we free the pointer we get from locate_in_PATH and then check whether it's NULL.
However, this is undefined behavior if the pointer is non-NULL, since the C standard no longer permits us to use a valid pointer after freeing it.
The only case in which the C standard would permit this to be defined behavior is if r were NULL, since it states that in such a case "no action occurs" as a result of calling free.
It's easy to suggest that this is not likely to be a problem, but we know that GCC does aggressively exploit the fact that undefined behavior can never occur to optimize and rewrite code, even when that's contrary to the expectations of the programmer.
It is, in fact, very common for it to omit NULL pointer checks, just as we have here.
Since it's easy to fix, let's do so, and avoid a potential headache in the future.
So instead of:
static int exists_in_PATH(const char *file)
{
char *r = locate_in_PATH(file);
free(r);
return r != NULL;
}
You now have:
static int exists_in_PATH(const char *file)
{
char *r = locate_in_PATH(file);
int found = r != NULL;
free(r);
return found;
}

trying to free allocated memory generate heap error

I'm dealing with a strange errors in code which i wrote in c.
this is the place where the error occur:
char* firstChar = (char*) malloc(ONE_CHAR_STRING);
if (!firstChar) {
*result = MTM_OUT_OF_MEMORY;
return false;
}
if (command != NULL) {
strcpy(firstChar, command);
firstChar[1] = '\0';
}
free(firstChar);
'command' is a string, and ONE_CHAR_STRING defines in the program, (ONE_CHAR_STRING= 2).
the error which appear when the program get into the 'free' function is:
warning: Heap block at 00731528 modified at 00731532 past requested size of 2
this error strangely append only on my PC/eclipse on windows. when i run the code in linux it doesn't prompt this error and works(the specific part) fine.
what could be the reason?
another question again about memory errors, how it is possibly that my program(without this part) works fine on windows, but in linux their is a problem in one of my memory allocations?
I can't write down here the code cause it's too long (and gdb doesn't gives me the lines of where the error occur).. the question is about the possibility and what could be the reasons for it.
Thanks, Almog.
you can use another string copy function to avoid overflow:
strncpy(firstChar,command,ONE_CHAR_STRING);
strcpy may overlap to copy firstChar if command string length is greater than ONE_CHAR_STRING or not null terminated that lead you to strange behavior. You can safely copy command string to firstChar by assign firstChar[0] = command[0]; firstChar[1] = '\0'
If your compiler for both Linux and Windows is gcc (MinGW in Windows) use -fstack-protector as compiler parameter to help you to debug function such as strcpy buffer overflow.

C segmentation faults due to fopen(). How to trace and what to look for?

(this was asked on ffmpeg-devel list, but counted way offtopic, so posting it here).
ffmpeg.c loads multiple .c's, that are using log.c's av_log -> av_log_default_callback function, that uses fputs;
void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl)
{
...
snprintf(line, sizeof(line), "[%s # %p] ", (*parent)->item_name(parent), parent);
... call to colored_fputs
Screen output:
static void colored_fputs(int level, const char *str){
...
fputs(str, stderr);
// this causes sigsegv just by fopen()
FILE * xFile;
xFile = fopen('yarr', 'w');
//fputs(str, xFile);fclose(xFile); // compile me. BOOM!
av_free(xFile); // last idea that came, using local free() version to avoid re-creatio
Each time, when fopen is put into code, it gives a segmentation fault of unknown reason. Why this kind of thing may happen here? Maybe due to blocking main I/O?
What are general 'blockers' that should be investigated in such a situation? Pthreads (involved in code somewhere)?
fopen takes strings as arguments, you're giving it char literals
xFile = fopen('yarr', 'w');
Should be
xFile = fopen("yarr", "w");
if(xFile == NULL) {
perror("fopen failed");
return;
}
The compiler should have warned about this, so make sure you've turned warning flags on (remeber to read them and fix them)

Resources