9

I'm trying to compile and run the below C program on my Ubuntu & Windows machines with both GCC & VC9. However, I'm facing below issues:

On Ubuntu machine:

GCC compiles fine, but when run, I'm shown this prompt:

Segmentation Fault (Core Dump).

On Windows machine:

VC9 Compiles & runs fine. GCC compiles fine, but process terminates when program is run.

Need your expert assistance here. Here is my code:

#include <string.h>
#include <stdio.h>

int calc_slope(int input1,int input2)
{
    int sum=0;
    int start=input1;
    int end=input2;
    int curr=start;

    //some validation:
    if (input1>input2)
        return -1;


    while(curr<=end)
    {
        if (curr>100)
        {
            char *s="";
            int length;
            int left;
            int right;
            int cent;

            sprintf(s,"%d",curr);
            length=strlen(s);
            s++;
            do
            {
                //printf("curr=%d char=%c pointer=%d length=%d \n",curr,*s,s,length);
                left = *(s-1) - '0';
                cent = *s - '0';
                right = *(s+1) - '0';
                //printf("curr=%d l=%d c=%d r=%d\n",curr,left,cent,right);
                if ( (cent>left && cent>right) || (cent<left && cent<right) )
                {
                    sum+=1; //we have either a maxima or a minima.
                }

                s++;
            } while (*(s+1)!='\0');
        }
        curr++;
    }

    return sum;
}

int main()
{
    printf("%d",calc_slope(1,150));
    return 0;
}

Update:

Credit goes to Eliah for not only helping me track the error, but also introducing me to gdb and its back-tracing tool (bt) which are so helpful in debugging a gcc compiled program. Here is the modified version, I worked up after some trial and error:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int calc_slope(int input1,int input2)
{
    int sum=0;
    int start=input1;
    int end=input2;
    int curr=start;

    //some validation:
    if (input1>input2)
        return -1;


    while(curr<=end)
    {
        if (curr>100)
        {
            int size=10;
            char *s=(char*)malloc((size+1) * sizeof(char));
            int left;
            int right;
            int cent;

            sprintf(s,"%d",curr);
            s++;
            do
            {
                left = *(s-1) - '0';
                cent = *s - '0';
                right = *(s+1) - '0';
                if ( (cent>left && cent>right) || (cent<left && cent<right) )
                {
                    sum+=1; //we have either a maxima or a minima.
                }

                s++;
            } while (*(s+1)!='\0');
        }
        curr++;
    }

    return sum;
}

int main()
{
    printf("%d",calc_slope(1,150));
    return 0;
}
8
  • 3
    I think it's not a compilation issue, but more of a runtime issue. You would get more help from StackOverflow.
    – oaskamay
    Mar 28, 2013 at 18:03
  • Are you sure this really runs fine after being compiled with VC9? Mar 28, 2013 at 18:31
  • Yes, 100%. but not with gcc. Mar 28, 2013 at 18:40
  • @PrahladYeri Cool! I've explained reasons for this in my answer. (This also means we should probably consider this question on-topic, since it's about Ubuntu*-specific* behavior. GCC in Windows exhibits comparable behavior but there is no error message and it's hard to know exactly what's happening there--furthermore, when GCC on Ubuntu and Microsoft Visual C++ work differently, I think Ask Ubuntu is a reasonable place to ask about why GCC on Ubuntu works as it does. With that said, further questions about how to do it right belong on Stack Overflow.) Mar 28, 2013 at 19:08
  • Modifying a string literal in C is undefined behavior. Please remember that.
    – jn1kk
    Mar 28, 2013 at 22:04

2 Answers 2

15

A segmentation fault occurs when a program tries to access memory outside of the area that has been allocated for it.

In this case, an experienced C programmer can see that the problem is happening in the line where sprintf is called. But if you can't tell where your segmentation fault is occurring, or if you don't want to bother reading through the code to try to figure it out, then you can build your program with debug symbols (with gcc, the -g flag does this) and then run it through a debugger.

I copied your source code and pasted it into a file I named slope.c. Then I built it like this:

gcc -Wall -g -o slope slope.c

(The -Wall is optional. It's just to make it produce warnings for more situations. This can help in figuring out what might be wrong, too.)

Then I ran the program in the debugger gdb by first running gdb ./slope to start gdb with the program, and then, once in the debugger, giving the run command to the debugger:

ek@Kip:~/source$ gdb ./slope
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/ek/source/slope...done.
(gdb) run
Starting program: /home/ek/source/slope 
warning: Cannot call inferior functions, you have broken Linux kernel i386 NX (non-executable pages) support!

Program received signal SIGSEGV, Segmentation fault.
0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6

(Don't worry about my you have broken Linux kernel i386 NX ... support message; it doesn't prevent gdb from being used effectively to debug this program.)

That information is highly cryptic...and if you don't have debug symbols installed for libc, then you'll get an even more cryptic message that has a hexadecimal address instead of the symbolic function name _IO_default_xsputn. Fortunately, it doesn't matter, because what we really want to know is where in your program the problem is happening.

So, the solution is to look backwards, to see what function calls took place leading up to that particular function call in a system library where the SIGSEGV signal was finally triggered.

gdb (and any debugger) has this feature built in: it's called a stack trace or backtrace. I use the bt debugger command to generate a backtrace in gdb:

(gdb) bt
#0  0x001a64cc in _IO_default_xsputn () from /lib/i386-linux-gnu/libc.so.6
#1  0x00178e04 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
#2  0x0019b234 in vsprintf () from /lib/i386-linux-gnu/libc.so.6
#3  0x0017ff7b in sprintf () from /lib/i386-linux-gnu/libc.so.6
#4  0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26
#5  0x08048578 in main () at slope.c:52
(gdb)

You can see that your main function calls the calc_slope function (which you've intended), and then calc_slope calls sprintf, which is (on this system) implemented with calls to a couple of other related library functions.

What you're generally interested in is the function call in your program that calls a function outside of your program. Unless there is a bug in the library/libraries themselves that you're using (in this case, the standard C library libc provided by the library file libc.so.6), the bug that causes the crash is in your program and often will be at or near the last call in your program.

In this case, that's:

#4  0x080484cc in calc_slope (input1=1, input2=150) at slope.c:26

That's where your program calls sprintf. We know this because sprintf is the next step up. But even without it stating that, you know this because that's what happens on line 26, and it says:

... at slope.c:26

In your program, line 26 contains:

            sprintf(s,"%d",curr);

(You should always use a text editor that automatically shows line numbers, at least for the line you are currently on. This is very helpful in interpreting both compile-time errors, and runtime problems revealed while using a debugger.)

As discussed in Dennis Kaarsemaker's answer, s is a one-byte array. (Not zero, because the value you've assigned it, "", is one byte long, that is, it is equal to { '\0' }, in the same way that "Hello, world!\n" is equal to { 'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n', '\0' }.)

So, why could this still work on some platform (and apparently does when compiled with VC9 for Windows)?

People often say that when you allocate memory and then try to access memory outside of it, that produces an error. But that's not really true. According to the C and C++ technical standards, what this really produces is undefined behavior.

In other words, anything can happen!

Still, some things are more likely than others. Why is it that a small array on the stack will, on some implementations, appear to work like a larger array on the stack?

This comes down to how stack allocation is implemented, which is permitted to vary from platform to platform. Your executable may allocate more memory to its stack than is actually intended to be used at any one time. Sometimes this may allow you to write to memory locations that you have not explicitly laid claim to in your code. It's very likely that this is what's happening when your build your program in VC9.

However, you should not rely on this behavior even in VC9. It could potentially depend on different versions of libraries that could exist on different Windows systems. But even more likely is the problem that the extra stack space is allocated with the intention that it will actually be used, and so it may actually be used. Then you experience the full nightmare of "undefined behavior," where, in this case, more than one variable could end up stored in the same place, where writing to one overwrites the other ...but not always, because sometimes writes to variables are cached in registers and not actually performed immediately (or reads to variables may be cached, or a variable may be assumed to be the same as it was before because the memory allocated to it is known by the compiler not to have been written to through the variable itself).

And that brings me to the other likely possibility for why the program worked when built with VC9. It's possible, and somewhat likely, that some array or other variable was actually allocated by your program (which can include being allocated by a library your program is using) to use the space after the one-byte array s. So then, treating s as an array longer than one byte would have the effect of accessing the contents of that/those variables/arrays, which could also be bad.

In conclusion, when you have a mistake like this, it's lucky to get an error like "Segmentation fault" or "General protection fault." When you don't have that, you may not find out until it is too late that your program has undefined behavior.

1
  • 1
    Thanks for such lucid explanation. This is precisely what I needed..!! Mar 28, 2013 at 19:15
9

Hello buffer overflow!

char *s="";
sprintf(s,"%d",curr);
length=strlen(s);

You allocate one byte for a string on the stack and then proceed to write more than one byte to it. And to top it off, you read beyond the end of that array. Please read a C manual and especially the section on strings and allocating memory for them.

4
  • Yes I came to know about that later. But when I wrote this, the VC9 compiler not only allowed, but also showed me the results properly. I printf-ed the strlen(s) and it showed me 4, not 1!! Mar 28, 2013 at 18:43
  • Could you also advice me how should I go about correcting this? As you must have surmised from code, I have no way of allocating fixed size to *s in advance. Its length is the number of digits in curr variable which cannot be known until I convert it to a string!! ? Mar 28, 2013 at 18:45
  • I can, but you really should head over to Stack Overflow for programming advise, as it's quite offtopic here. Mar 28, 2013 at 19:00
  • 1
    @DennisKaarsemaker The original question here might not be off-topic since it apparently involves behavior that differs between Ubuntu and another platform (and I've explained the most likely reason for this in my answer). I do agree that questions about how to properly allocate strings in C belong on Stack Overflow and not here. Mar 28, 2013 at 19:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .