Coding in C - a summary of some popular mistakes
Results 1 to 5 of 5

Thread: Coding in C - a summary of some popular mistakes

  1. #1
    Senior Member
    Join Date
    May 2003

    Coding in C - a summary of some popular mistakes

    I am confuse ...should it be in programming security or Tutorials forum....
    if moderater feels to move pls move it......but i thought it was more related to programming security...than being a tute...any just y views.

    First of all, in this paper, I'm not going to verbosely talk about
    secure and fail-safe coding practice, nor security code auditing.
    These are interesting topics, for which many good papers and FAQs are
    out, but my goal here is just to introduce you to some common, but nasty
    errors, which are not all related to security. Instead, they are errors
    that I made in the past or noticed that they could easily be made. Most
    of them are not detected at compile time with all warnings enabled, which
    makes them very nasty and hard to detect. If you have experience in writing
    security relevant (e.g. suid) programs and properly check them, you probably
    will notice them as well sooner or later. But it is the "little things" in
    debugging and re-coding that are time expensive, so maybe this summary will
    help you detecting these errors faster, or preventing them, thus saving your
    time for the stability, performance and security relevant code improving.
    I'm going to start with some real simple things, and will then be trying
    to cover some errors which can be trickier to spot.

    Most of the widespread program bugs that cause unexpected behavior are
    caused by the arithmetic notation of C, which can seem ambiguous to people
    who aren't used to the language. When fundamental and detailed experience with
    the operations are made, comparison operations and numbering schemes in C
    actually reveal to be very practicable and useful. For example, it is
    important to realize that every iteration in C starts with 0 as the first
    positive integer value, not 1. The address referenced by array[0] contains
    the first value. Pointers always directly point to the beginning of an item,
    since in C, they are "real" pointers, e.g. the actual address references
    the machine uses internally. If you allocate space with "char array[10];",
    you are actually allocating 10 items, of which the first one is array[0],
    the last usable one is array[9], and in character buffers, array[10] contains
    a delimiting binary zero. This delimiter can easily be overwritten, by
    using bad loops to parse data, e.g.: for(i=0;i<=10;++i) array[i] = ...
    Note that this will overwrite array[10], and therefore remove the separating
    zero. If that happens to a character string, most string parsing functions
    can no longer identify its bounds, and will output memory after the end
    of the array until the next binary zero in memory is found. This can
    result in undefined output, and can be hard to track down in the source.

    Something else are format errors. Most functions that use variable arguments
    (see stdarg(3)), also use format to parse variables into a format string
    specified in the code. Common mistakes are made while parsing signed and
    unsigned variables with wrong format strings. Unsigned variables cannot
    represent values smaller than zero, instead, their value can be twice as large
    as their signed equivalent. Subtracting one from zero makes an unsigned variable
    represent the largest value possible (Ex.: 0x000000 - 0x1 = 0xffffff ). This
    makes bad conversions a dangerous thing. For a signed int, use %d. For a signed
    long use %ld. For unsigned int use %u, for parsing the hex representation of
    the value %x, and so on. This is verbosely explained on the manpage.

    Another important thing you can mess up with is sizeof. Sizeof is an expression,
    not a function, and it is often evaluated by the compiler only inside the scope
    of the current function. Mind that sizeof references the full address space
    for a variable or pointer which the compiler can recognize at compile time.
    Using sizeof on a character buffer like "char buf[1234]; ... sizeof(buf);"
    returns the value 1234. However, if a buffer or array is either dynamically
    allocated, or if it is allocated or created outside of the function in which
    sizeof is used, the sizeof command will NOT and cannot reference the address range that
    the pointer references. Instead, it will return the size that the pointer
    occupies in memory. For example, try compiling and running this program:
    p(char *buf){printf("%d\n",sizeof(buf));}main(){char buf[100];p(buf);}

    Generally, ambiguous compiler expressions which are not being regarded as
    such, can be one of the biggest problem during the tracking of persistent bugs
    or unwanted behavior. A good trivial example is the negation ('!' expression).
    This is a binary, not a real arithmetic operation, and it only differentiates
    between zero and non-zero. !1 is exactly the same as !100 or !-100, zero.
    This means that in some cases, it is not recommended to rely on this
    expression, e.g. when checking if 'i' is smaller than or equal to
    zero, it would be simply wrong to use "if (!i) ...".

    Besides these things, it is very important to closely study the behavior
    of any library or system functions that one uses frequently. Most of the time,
    the documentation for functions is accurate, but the detailed behavior of
    a function in all situations, as well as its conformance to established
    standards can be important. For example, the select() call waits for a
    change of status on a specified amount of sockets. However, this can often be
    a change that doesn not necessarily indicate an established connection. I've
    seen programs that try to wait for a socket becoming ready to read from and
    then assume a connection is established. However, it could have returned an
    error or disconnected immediately again. Doing a getpeername operation is
    recommended in this case. What I'm trying to say is that functions, especially
    system calls, provide a documented behavior, which however is not always
    reliable, and that documented exceptions are possible to occur, and those
    exceptions should always be handled in advance in a stable program.

    System events and signal handling represent another whole category of
    problems. In fact, a program that has to deal with all external events and
    signals, or uses many of them internally, e.g. for multithreading, is
    beyond the scope of a single paper. Only practical programming and testing
    experience can help you to develop good skills with handling these events.
    One interesting example are the alarm calls/timers and signals. The easiest
    way to handle timeouts is to setup a signal handler for alarm, then change
    the restart handler for blocking functions with siginterrupt() to return -1,
    when returning from a handled alarm event. A more complex way of alarm
    handling is to make use of setjmp/longjmp, which can save and return to a
    execution context anywhere in a program. While they are easy to implement,
    these commands are actually very complex in their behavior and should be
    used with care. An experience I made is that due to alarm handlers, programs
    can be brought back to functions that are restarting on timeouts (the
    default for blocking functions), and therefore repeat actions that the
    programmer never intended to perform more than once. This mistake is commonly
    made by programmers who use poor style as for example alarm(5) then connect()
    and try to limit the connect() time this way. In this case non blocking
    sockets are the better alternative. As it shows, the more sophisticated
    functions a programmer uses, the harder can errors be found and tracked.

    As a last advice for writing good code, using compiler checks are
    recommended, such as -Wimplicit, -Wall, -ansi, -pedantic, as well as always
    using prototypes, or even designing a concept before designing a more
    complicated program. It is also a good experience to try and port a program
    to many other platforms, as other errors might be revealed on other
    systems. If security and stability is very important, for example in a
    suid application, or if writing a big server program, it is recommended to
    use compiler parser generators (yacc, bison, etc. which are, however, probably
    some of the most complex programs to use ever), or lint-based automatic code
    checking systems (e.g. lclint), which provide source code checking
    at any desired strictness level.
    Source :
    guru@linux:~> who I grep -i blonde I talk; cd ~; wine; talk; touch; unzip; touch; strip; gasp; finger; mount; fsck; more; yes; gasp; umount; make clean; sleep;

  2. #2
    Senior Member
    Join Date
    Nov 2003
    Wow. A quasi-tutorial on C. Plain C. Not C++, Visual C++ or C#. Hard-core C Coder, or is this just what you dug up?

    Just curious, how'd you find it?
    There is a ghost in the machine, and he is my friend.

  3. #3
    Senior Member
    Join Date
    May 2002
    sonofgalen, he found it here:
    You were right in posting it here...the tutorials section of AO is for original work only, and because you copied this off of a website, it isnt original ...nice post
    Support your right to arm bears.

    ^^This was the first video game which i played on an old win3.1 box

  4. #4
    Senior Member
    Join Date
    Nov 2003
    I know where he found it. That wasn't my question. My question was "How did you find it?" Was he just browsing that website, did he do a search, was it on a link? What? Either way, good stuff.
    There is a ghost in the machine, and he is my friend.

  5. #5
    Senior Member
    Join Date
    May 2003
    SonofGalen i was searching for Buffer Overflow Exploit tutes, one of them is written by mixter, so i decided to browse hos entire site....he has quite good stuff out there.
    guru@linux:~> who I grep -i blonde I talk; cd ~; wine; talk; touch; unzip; touch; strip; gasp; finger; mount; fsck; more; yes; gasp; umount; make clean; sleep;

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts

 Security News


       Security Trends


           Buying Guides