Strange behaviour

Discussion in 'C Programming' started by gamehack, Apr 25, 2006.

  1. gamehack

    gamehack Guest

    Hi all,

    I've been testing out a small function and surprisingly it does not
    work okay. Here's the full code listing:
    #include "stdlib.h"
    #include "stdio.h"

    char* escaped_byte_cstr_ref(char byte);

    int main (int argc, const char * argv[])
    {
    char* t = escaped_byte_cstr_ref('!');
    printf(t);
    free(t);

    return 0;
    }

    char* escaped_byte_cstr_ref(char byte)
    {
    char buff[3];
    sprintf(buff, "%X", byte);

    char* str = malloc(4);

    str[0] = '%';
    str[1] = buff[0];
    str[2] = buff[1];
    str[3] = 0x0;

    return str;
    }

    The program does not print anything - neither in bash nor in gdb.
    Although I do remember getting it running on XP with migw(currently I'm
    testing on OS X).

    Thanks for your help,
    gamehack
    gamehack, Apr 25, 2006
    #1
    1. Advertising

  2. gamehack

    Ian Collins Guest

    gamehack wrote:
    > Hi all,
    >
    > I've been testing out a small function and surprisingly it does not
    > work okay. Here's the full code listing:
    > #include "stdlib.h"
    > #include "stdio.h"
    >
    > char* escaped_byte_cstr_ref(char byte);
    >
    > int main (int argc, const char * argv[])
    > {
    > char* t = escaped_byte_cstr_ref('!');
    > printf(t);


    printf ("%s\n", t);

    Otherwise you don't flush stdout.

    --
    Ian Collins.
    Ian Collins, Apr 25, 2006
    #2
    1. Advertising

  3. gamehack wrote:
    > Hi all,
    >
    > I've been testing out a small function and surprisingly it does not
    > work okay. Here's the full code listing:
    > #include "stdlib.h"
    > #include "stdio.h"
    >


    To ensure that you are using standard headers and not ones in your
    working directory, use
    #include <stdio.h>
    #include <stdlib.h>

    > char* escaped_byte_cstr_ref(char byte);
    >
    > int main (int argc, const char * argv[])
    > {
    > char* t = escaped_byte_cstr_ref('!');
    > printf(t);
    > free(t);
    >
    > return 0;
    > }
    >

    All good so far.

    > char* escaped_byte_cstr_ref(char byte)
    > {
    > char buff[3];
    > sprintf(buff, "%X", byte);
    >

    To simply copy a string, use strcpy(char *dest, char *source);
    I'm not sure that you used the correct printf argument (%X).
    > char* str = malloc(4);
    >
    > str[0] = '%';
    > str[1] = buff[0];
    > str[2] = buff[1];
    > str[3] = 0x0;
    >

    This is okay, but the intermediate variable buff is unneccessary, unless
    you need to validate the input.
    > return str;
    > }
    >
    > The program does not print anything - neither in bash nor in gdb.
    > Although I do remember getting it running on XP with migw(currently I'm
    > testing on OS X).


    Try inserting my modifications, and see if that fixes it.
    Andrew Poelstra, Apr 25, 2006
    #3
  4. Ian Collins wrote:
    > gamehack wrote:
    >> Hi all,
    >>
    >> I've been testing out a small function and surprisingly it does not
    >> work okay. Here's the full code listing:
    >> #include "stdlib.h"
    >> #include "stdio.h"
    >>
    >> char* escaped_byte_cstr_ref(char byte);
    >>
    >> int main (int argc, const char * argv[])
    >> {
    >> char* t = escaped_byte_cstr_ref('!');
    >> printf(t);

    >
    > printf ("%s\n", t);
    >
    > Otherwise you don't flush stdout.
    >


    Disregard my last post; it appears that

    printf (t);

    should be

    printf ("%s", t);
    Andrew Poelstra, Apr 25, 2006
    #4
  5. gamehack

    Ian Collins Guest

    Andrew Poelstra wrote:
    > gamehack wrote:
    >
    >> char* escaped_byte_cstr_ref(char byte)
    >> {
    >> char buff[3];
    >> sprintf(buff, "%X", byte);
    >>

    > To simply copy a string, use strcpy(char *dest, char *source);
    > I'm not sure that you used the correct printf argument (%X).
    >

    The OP wants the value in hex, not a copy. It would have been clearer
    if the function had a sensible name!

    >> char* str = malloc(4);
    >>
    >> str[0] = '%';
    >> str[1] = buff[0];
    >> str[2] = buff[1];
    >> str[3] = 0x0;
    >>

    > This is okay, but the intermediate variable buff is unneccessary, unless
    > you need to validate the input.
    >

    See above.

    --
    Ian Collins.
    Ian Collins, Apr 25, 2006
    #5
  6. gamehack

    cane Guest

    gamehack ha scritto:

    > The program does not print anything - neither in bash nor in gdb.
    > Although I do remember getting it running on XP with migw(currently I'm
    > testing on OS X).


    this works:

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

    char* escaped_byte_cstr_ref(char byte);

    int main (int argc, const char * argv[])
    {
    char* t = escaped_byte_cstr_ref('!');
    printf("%s",t);
    free(t);

    return 0;
    }

    char* escaped_byte_cstr_ref(char byte)
    {
    char buff[3];
    char* str = malloc(4);

    sprintf(buff, "%X", byte);

    str[0] = '%';
    str[1] = buff[0];
    str[2] = buff[1];
    str[3] = 0x0;

    return str;
    }
    cane, Apr 26, 2006
    #6
  7. gamehack

    Ian Collins Guest

    Ian Collins wrote:
    > gamehack wrote:
    >
    >>Hi all,
    >>
    >>I've been testing out a small function and surprisingly it does not
    >>work okay. Here's the full code listing:
    >>#include "stdlib.h"
    >>#include "stdio.h"
    >>
    >>char* escaped_byte_cstr_ref(char byte);
    >>
    >>int main (int argc, const char * argv[])
    >>{
    >> char* t = escaped_byte_cstr_ref('!');
    >> printf(t);

    >
    >
    > printf ("%s\n", t);
    >
    > Otherwise you don't flush stdout.
    >

    Right fix, wrong explanation, sorry.

    The first argument to printf is a format string.

    --
    Ian Collins.
    Ian Collins, Apr 26, 2006
    #7
  8. gamehack

    Old Wolf Guest

    gamehack wrote:
    > char* escaped_byte_cstr_ref(char byte)
    > {
    > char buff[3];
    > sprintf(buff, "%X", byte);


    Undefined behaviour -- %X expects an unsigned int, but you
    pass it a char. Also, you cause a buffer overflow if byte has
    any value other than 0 - 99 (ie. values greater than 99, and
    values less than 0). In the case of negative bytes, the
    buffer overflow will be particularly bad, as -1 will output
    FFFFFFFF if you are on IA32 architecture.

    Either use snprintf, or this:

    char buff[50];
    sprintf(buff, "%X", (unsigned int)byte);

    where '50' is larger than any int will be in the foreseeable future.
    Old Wolf, Apr 26, 2006
    #8
  9. Old Wolf wrote:
    > gamehack wrote:
    >> char* escaped_byte_cstr_ref(char byte)
    >> {
    >> char buff[3];
    >> sprintf(buff, "%X", byte);

    >
    > Undefined behaviour -- %X expects an unsigned int, but you
    > pass it a char. Also, you cause a buffer overflow if byte has
    > any value other than 0 - 99 (ie. values greater than 99, and
    > values less than 0).
    > In the case of negative bytes, the
    > buffer overflow will be particularly bad, as -1 will output
    > FFFFFFFF if you are on IA32 architecture.
    >
    > Either use snprintf, or this:
    >
    > char buff[50];
    > sprintf(buff, "%X", (unsigned int)byte);
    >
    > where '50' is larger than any int will be in the foreseeable future.
    >


    Let's assume that we have an unsigned char (which is a false assumption
    in most cases).

    A 3-element array can take a 2-digit string, which in this case will be
    a hex number. All the values that fit into uchar (0x00-0xFF) fit this
    criteria.

    It is true, however, that negative numbers will expand very uncleanly.
    Andrew Poelstra, Apr 26, 2006
    #9
  10. Andrew Poelstra wrote:
    > Old Wolf wrote:
    >> gamehack wrote:
    >>> char* escaped_byte_cstr_ref(char byte)
    >>> {
    >>> char buff[3];
    >>> sprintf(buff, "%X", byte);

    >>
    >> Undefined behaviour -- %X expects an unsigned int, but you
    >> pass it a char. Also, you cause a buffer overflow if byte has
    >> any value other than 0 - 99 (ie. values greater than 99, and
    >> values less than 0).
    >> In the case of negative bytes, the
    >> buffer overflow will be particularly bad, as -1 will output
    >> FFFFFFFF if you are on IA32 architecture.
    >>
    >> Either use snprintf, or this:
    >>
    >> char buff[50];
    >> sprintf(buff, "%X", (unsigned int)byte);
    >>
    >> where '50' is larger than any int will be in the foreseeable future.
    >>

    >
    > Let's assume that we have an unsigned char (which is a false assumption
    > in most cases).
    >
    > A 3-element array can take a 2-digit string, which in this case will be
    > a hex number. All the values that fit into uchar (0x00-0xFF) fit this
    > criteria.
    >
    > It is true, however, that negative numbers will expand very uncleanly.


    Note: no standard-ASCII character will exceed 0x7F, and therefore the
    negative number problem is practically nonexistant. Of course, in theory
    it is possible that this function will be passed bad data, and so
    precautions should be taken (in this case, simply using an unsigned char
    will eliminate all issues, because anything bigger than 255 will be
    wrapped).
    Andrew Poelstra, Apr 26, 2006
    #10
  11. In article <> "Old Wolf" <> writes:
    > gamehack wrote:
    > > char* escaped_byte_cstr_ref(char byte)
    > > {
    > > char buff[3];
    > > sprintf(buff, "%X", byte);

    >
    > Undefined behaviour -- %X expects an unsigned int, but you
    > pass it a char.


    Not so. An int is passed. You can not pass chars to a variadic function.
    However, changing "char byte" above to "unsigned char byte" above would be
    an improvement.

    > pass it a char. Also, you cause a buffer overflow if byte has
    > any value other than 0 - 99 (ie. values greater than 99, and
    > values less than 0).


    I have no idea where you base that "99" on. There will be problems if
    it is larger than 255 or smaller than 0.

    > Either use snprintf, or this:
    >
    > char buff[50];
    > sprintf(buff, "%X", (unsigned int)byte);
    >
    > where '50' is larger than any int will be in the foreseeable future.


    This is the wrong solution. Consider what happens if byte == -1.
    --
    dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
    home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
    Dik T. Winter, Apr 26, 2006
    #11
  12. gamehack

    Old Wolf Guest

    Dik T. Winter wrote:
    > "Old Wolf" <> writes:
    >> gamehack wrote:
    >>> char* escaped_byte_cstr_ref(char byte)
    >>> {
    >>> char buff[3];
    >>> sprintf(buff, "%X", byte);

    >>
    >> Undefined behaviour -- %X expects an unsigned int, but you
    >> pass it a char.

    >
    > Not so. An int is passed.


    C99 7.19.6.1#9 says:

    If any argument is not the correct type for the corresponding
    conversion specification, the behaviour is undefined.

    3.3 defines "argument" as:

    expression in the comma-separated list bounded by the
    parentheses in a function call expression ...

    6.5.2.2#6 defines the default argument promotions:

    the integer promotions are performed on each argument,
    and arguments that have type float are promoted to double.

    The latter two quotes support the hypothesis that the "argument"
    is the expression before the default promotions occur (as
    opposed to the result of the default argument promotions).

    In the above code, the argument is the expression:

    byte

    and it undoubtedly has type char, thus falling foul of 7.19.6.1#9 .


    >
    >> pass it a char. Also, you cause a buffer overflow if byte has
    >> any value other than 0 - 99 (ie. values greater than 99, and
    >> values less than 0).

    >
    > I have no idea where you base that "99" on. There will be
    > problems if it is larger than 255 or smaller than 0.


    Sorry, I was thinking of printing in decimal, for some reason.

    >
    >> Either use snprintf, or this:
    >>
    >> char buff[50];
    >> sprintf(buff, "%X", (unsigned int)byte);
    >>
    >> where '50' is larger than any int will be in the foreseeable future.

    >
    > This is the wrong solution. Consider what happens if byte == -1.


    Then there will be no buffer overflow, unless the unsigned int
    has more than 4*49 = 196 bits, which I don't foresee happening.

    (I'm not speculating on what the intent of the code is -- just
    making sure it doesn't cause a buffer overflow. The OP clearly
    did not consider the negative-char case anyway, so he is
    welcome to start considering it now).
    Old Wolf, Apr 26, 2006
    #12
  13. Old Wolf wrote:
    > Dik T. Winter wrote:
    >> "Old Wolf" <> writes:
    >>> gamehack wrote:
    >>>> char* escaped_byte_cstr_ref(char byte)
    >>>> {
    >>>> char buff[3];
    >>>> sprintf(buff, "%X", byte);
    >>> Undefined behaviour -- %X expects an unsigned int, but you
    >>> pass it a char.

    >> Not so. An int is passed.

    >
    > C99 7.19.6.1#9 says:
    >
    > If any argument is not the correct type for the corresponding
    > conversion specification, the behaviour is undefined.
    >
    > 3.3 defines "argument" as:
    >
    > expression in the comma-separated list bounded by the
    > parentheses in a function call expression ...
    >
    > 6.5.2.2#6 defines the default argument promotions:
    >
    > the integer promotions are performed on each argument,
    > and arguments that have type float are promoted to double.
    >
    > The latter two quotes support the hypothesis that the "argument"
    > is the expression before the default promotions occur (as
    > opposed to the result of the default argument promotions).
    >
    > In the above code, the argument is the expression:
    >
    > byte
    >
    > and it undoubtedly has type char, thus falling foul of 7.19.6.1#9 .
    >

    No, a char is a type of int. Therefore, it will be promoted to an
    unsigned long int, or whatever it expects.
    Andrew Poelstra, Apr 26, 2006
    #13
  14. gamehack

    Ian Collins Guest

    Old Wolf wrote:
    > Dik T. Winter wrote:
    >
    >>"Old Wolf" <> writes:
    >>
    >>>gamehack wrote:
    >>>
    >>>>char* escaped_byte_cstr_ref(char byte)
    >>>>{
    >>>> char buff[3];
    >>>> sprintf(buff, "%X", byte);
    >>>
    >>>Undefined behaviour -- %X expects an unsigned int, but you
    >>>pass it a char.

    >>
    >>Not so. An int is passed.

    >
    >
    > C99 7.19.6.1#9 says:
    >
    > If any argument is not the correct type for the corresponding
    > conversion specification, the behaviour is undefined.
    >

    But char is of the correct type, it will be promoted to int.

    extern void fn( int );

    char m;
    short n;
    int o;
    fn( m );
    fn( n );
    fn( o );

    Are all correct.

    --
    Ian Collins.
    Ian Collins, Apr 26, 2006
    #14
  15. Andrew Poelstra <> writes:
    > Old Wolf wrote:
    >> Dik T. Winter wrote:
    >>> "Old Wolf" <> writes:
    >>>> gamehack wrote:
    >>>>> char* escaped_byte_cstr_ref(char byte)
    >>>>> {
    >>>>> char buff[3];
    >>>>> sprintf(buff, "%X", byte);
    >>>> Undefined behaviour -- %X expects an unsigned int, but you
    >>>> pass it a char.
    >>> Not so. An int is passed.

    >> C99 7.19.6.1#9 says:
    >> If any argument is not the correct type for the corresponding
    >> conversion specification, the behaviour is undefined.
    >> 3.3 defines "argument" as:
    >> expression in the comma-separated list bounded by the
    >> parentheses in a function call expression ...
    >> 6.5.2.2#6 defines the default argument promotions:
    >> the integer promotions are performed on each argument,
    >> and arguments that have type float are promoted to double.
    >> The latter two quotes support the hypothesis that the "argument"
    >> is the expression before the default promotions occur (as
    >> opposed to the result of the default argument promotions).
    >> In the above code, the argument is the expression:
    >> byte
    >> and it undoubtedly has type char, thus falling foul of 7.19.6.1#9 .
    >>

    > No, a char is a type of int. Therefore, it will be promoted to an
    > unsigned long int, or whatever it expects.


    Wrong, and wrong.

    char is an integer type; int is another integer type. A char is not
    "a type of int". (Are you thinking of the fact that character
    literals are of type int?)

    In the following:

    void foo(unsigned long x);
    ...
    char c;
    foo(c);

    c is converted to type unsigned long. This kind of conversion does
    not take place for variadic functions like sprintf. In that case,
    only the default argument promotions are performed; char is promoted
    to int, regardless of the type specified by the format string. If the
    argument is of type char, and the format string specifies a type other
    than int, the behavior is undefined.

    (If CHAR_MAX==UINT_MAX, char is promoted to unsigned int rather than
    int; this can happen only if plain char is unsigned and
    sizeof(int)==1, which can happen only if CHAR_BIT>=16.)

    (Passing a signed int when an unsigned int is expected, or vice versa,
    is likely to work as long as the value is within the range of both
    types; the standard almost guarantees this, but doesn't *quite* say
    so.)

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
    Keith Thompson, Apr 26, 2006
    #15
  16. gamehack

    Guest

    gamehack wrote:
    > Hi all,
    >
    > I've been testing out a small function and surprisingly it does not
    > work okay. Here's the full code listing:
    > #include "stdlib.h"
    > #include "stdio.h"
    >
    > char* escaped_byte_cstr_ref(char byte);
    >
    > int main (int argc, const char * argv[])
    > {
    > char* t = escaped_byte_cstr_ref('!');
    > printf(t);
    > free(t);
    >
    > return 0;
    > }
    >
    > char* escaped_byte_cstr_ref(char byte)
    > {
    > char buff[3];
    > sprintf(buff, "%X", byte);
    >
    > char* str = malloc(4);
    >
    > str[0] = '%';
    > str[1] = buff[0];
    > str[2] = buff[1];
    > str[3] = 0x0;
    >
    > return str;
    > }
    >
    > The program does not print anything - neither in bash nor in gdb.
    > Although I do remember getting it running on XP with migw(currently I'm
    > testing on OS X).
    >
    > Thanks for your help,
    > gamehack


    Thanks a lot everyone. I did a really stupid mistake.

    Kind regards,
    gamehack
    , Apr 26, 2006
    #16
  17. gamehack

    Old Wolf Guest

    Ian Collins wrote:
    > Old Wolf wrote:
    >> C99 7.19.6.1#9 says:
    >>
    >> If any argument is not the correct type for the corresponding
    >> conversion specification, the behaviour is undefined.
    >>

    > But char is of the correct type, it will be promoted to int.
    >
    > extern void fn( int );
    >


    The section I quoted is talking about arguments to variadic
    functions that match the '...' bit.
    Old Wolf, Apr 26, 2006
    #17
  18. In article <> "Old Wolf" <> writes:
    > Dik T. Winter wrote:
    > > "Old Wolf" <> writes:
    > >> gamehack wrote:
    > >>> char* escaped_byte_cstr_ref(char byte)
    > >>> {
    > >>> char buff[3];
    > >>> sprintf(buff, "%X", byte);
    > >>
    > >> Undefined behaviour -- %X expects an unsigned int, but you
    > >> pass it a char.

    > >
    > > Not so. An int is passed.

    >
    > C99 7.19.6.1#9 says:
    > If any argument is not the correct type for the corresponding
    > conversion specification, the behaviour is undefined.


    So according to your reasoning the following:
    char c = 'a';
    printf("%c\n", c);
    is undefined behaviour. (%c expects an int argument, see that section #8.)

    > 3.3 defines "argument" as:
    > expression in the comma-separated list bounded by the
    > parentheses in a function call expression ...
    >
    > 6.5.2.2#6 defines the default argument promotions:
    > the integer promotions are performed on each argument,
    > and arguments that have type float are promoted to double.
    >
    > The latter two quotes support the hypothesis that the "argument"
    > is the expression before the default promotions occur (as
    > opposed to the result of the default argument promotions).


    The argument is, but the type of the argument is not.
    --
    dik t. winter, cwi, kruislaan 413, 1098 sj amsterdam, nederland, +31205924131
    home: bovenover 215, 1025 jn amsterdam, nederland; http://www.cwi.nl/~dik/
    Dik T. Winter, Apr 27, 2006
    #18
  19. gamehack

    Old Wolf Guest

    Dik T. Winter wrote:
    > "Old Wolf" <> writes:
    >>
    >> C99 7.19.6.1#9 says:
    >> If any argument is not the correct type for the corresponding
    >> conversion specification, the behaviour is undefined.

    >
    > So according to your reasoning the following:
    > char c = 'a';
    > printf("%c\n", c);
    > is undefined behaviour. (%c expects an int argument, see that section #8.)


    It looks that way. Section 7 says:
    hh Specifies that a following d, i, o, u, x, or X conversion
    specifier applies to a signed char or unsigned char
    argument

    h Specifies that a following d, i, o, u, x, or X conversion
    specifier applies to a short int or unsigned short int
    argument

    By your reasoning these modifiers could never be used, as
    the argument is always an int (assuming we are talking
    about systems where chars and shorts promote to int).

    I think that it is inconsistent to read "argument" as the pre-
    promotion argument in some clauses, and to read it as the
    post-promotion argument in other clauses of the same
    section.

    In fact, those section 7 entries have a suffix clarifying that
    they are talking about the pre-promotion argument. Perhaps
    a DR is in order for section 8 to clarify that your code
    should not cause UB.


    >> The latter two quotes support the hypothesis that the "argument"
    >> is the expression before the default promotions occur (as
    >> opposed to the result of the default argument promotions).

    >
    > The argument is, but the type of the argument is not.


    In your above code, the argument is 'a' of type char. After the
    default argument promotions, the argument is 'a' of type int.
    Right?
    Old Wolf, Apr 27, 2006
    #19
  20. gamehack

    pete Guest

    Old Wolf wrote:
    >
    > Dik T. Winter wrote:
    > > "Old Wolf" <> writes:
    > >>
    > >> C99 7.19.6.1#9 says:
    > >> If any argument is not the correct type for the corresponding
    > >> conversion specification, the behaviour is undefined.

    > >
    > > So according to your reasoning the following:
    > > char c = 'a';
    > > printf("%c\n", c);
    > > is undefined behaviour.
    > > (%c expects an int argument, see that section #8.)

    >
    > It looks that way.


    C99
    6.3.1 Arithmetic operands
    6.3.1.1 Boolean, characters, and integers

    2 The following may be used in an expression
    wherever an int or unsigned int may be used:

    — An object or expression with an integer type
    whose integer conversion rank is less than
    the rank of int and unsigned int.

    --
    pete
    pete, Apr 27, 2006
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Antonio

    Strange encoding behaviour

    Antonio, Dec 29, 2004, in forum: ASP .Net
    Replies:
    0
    Views:
    414
    Antonio
    Dec 29, 2004
  2. Jan
    Replies:
    2
    Views:
    1,414
    Mike Treseler
    Dec 16, 2004
  3. David Cantin

    Strange behaviour with perl and apache

    David Cantin, Nov 3, 2003, in forum: Perl
    Replies:
    1
    Views:
    448
    Jim Gibson
    Nov 3, 2003
  4. Dennis Johansson
    Replies:
    1
    Views:
    490
    Dennis Johansson
    Aug 21, 2003
  5. Andy Chambers
    Replies:
    1
    Views:
    374
    Daniel Dyer
    May 14, 2007
Loading...

Share This Page