Crash when calling va_arg()

  • Hello,

    I've hit a wall while experimenting with variadic functions and blocks. The code works pretty well until the second block is executed. After that, it crashes the next time va_arg() gets called. Here's the code:

    #import <Foundation/Foundation.h>

    typedef id (^fooBlock)(id result, NSError **error);
    void blockStep(fooBlock firstBlock, ...);

    int main(int argc, const char * argv[])
    {
        @autoreleasepool {

            blockStep (
                      ^ (id result, NSError **error) {
                          NSMutableString *value = [NSMutableString new];
                          [value appendString:@"One!"];
                          return value;
                      },
                      ^ (id result, NSError **error) {
                          NSMutableString *value = [NSMutableString new];
                          if (nil != result) {
                              [value appendString:result];
                              [value appendString:@", "];
                          }
                          [value appendString:@"Two!"];
                          return value;
                      }
            );

        }
        return 0;
    }

    #pragma mark -

    void blockStep(fooBlock firstBlock, ...)
    {
        va_list args;
        va_start(args, firstBlock);
        id result = nil;

        do {
            result = firstBlock(result, nil);
            NSLog(@"%@", result);
        } while (nil != (firstBlock = va_arg(args, fooBlock)));

        va_end(args);
    }

    The output looks like this:

    > 2012-07-04 16:18:40.000 BlockStep[12418:303] One!
    > 2012-07-04 16:18:56.533 BlockStep[12418:303] One!, Two!

    I've eliminated the crash by adding a nil sentinel in blockStep():

            blockStep (
                      ^ (id result, NSError **error) {
                          NSMutableString *value = [NSMutableString new];
                          [value appendString:@"One!"];
                          return value;
                      },
                      ^ (id result, NSError **error) {
                          NSMutableString *value = [NSMutableString new];
                          if (nil != result) {
                              [value appendString:result];
                              [value appendString:@", "];
                          }
                          [value appendString:@"Two!"];
                          return value;
                      },
                      nil
            );

    This allows it to work without crashing, but I'd like if possible to avoid having to place the sentinel. Any ideas?

    Thanks in advance,

    -- Tito
  • On Jul 4, 2012, at 4:30 PM, Tito Ciuro wrote:

    > This allows it to work without crashing, but I'd like if possible to avoid having to place the sentinel. Any ideas?

    There's no way around this, other than perhaps passing in the number of variable arguments as an explicit parameter. The C calling conventions don't allow a function to detect how many arguments it has, or where the argument list ends in the stack. So va_arg() can't magically return NULL at the end of the argument list, as your code expects it to.

    —Jens
  • On 4 Jul 2012, at 6:30 PM, Tito Ciuro wrote:

    > void blockStep(fooBlock firstBlock, ...)
    > {
    > va_list args;
    > va_start(args, firstBlock);
    > id result = nil;
    >
    > do {
    > result = firstBlock(result, nil);
    > NSLog(@"%@", result);
    > } while (nil != (firstBlock = va_arg(args, fooBlock)));
    >
    > va_end(args);
    > }
    >
    > The output looks like this:
    >
    >> 2012-07-04 16:18:40.000 BlockStep[12418:303] One!
    >> 2012-07-04 16:18:56.533 BlockStep[12418:303] One!, Two!
    >
    > I've eliminated the crash by adding a nil sentinel in blockStep():
    >
    > blockStep (
    > ^ (id result, NSError **error) {
    > NSMutableString *value = [NSMutableString new];
    > [value appendString:@"One!"];
    > return value;
    > },
    > ^ (id result, NSError **error) {
    > NSMutableString *value = [NSMutableString new];
    > if (nil != result) {
    > [value appendString:result];
    > [value appendString:@", "];
    > }
    > [value appendString:@"Two!"];
    > return value;
    > },
    > nil
    > );

    >
    > This allows it to work without crashing, but I'd like if possible to avoid having to place the sentinel. Any ideas?

    Not possible.

    Notionally, all parameters are passed to C functions in memory on a stack, which is unformatted and could contain anything. A function has no way of knowing how many parameters have been pushed onto the stack, or where the memory trails off into saved processor state and the like, or the types, or the amounts of memory they subtend….

    Most variadic functions require sentinels (usually NULL) to tell them to stop looking for parameters. The best-known exceptions are printf()-family functions, which know what to find on the stack because the format string tells them.

    — F

    --
    Fritz Anderson
    Xcode 4 Unleashed: Don't bring your bathroom copy into the kitchen — were you raised in a barn?
    <http://x4u.manoverboard.org/>
  • Hi Fritz and Jens,

    It makes total sense now. Out of the two options (NULL sentinel vs a number to indicate the number of args), I would choose NULL because out of the two, it's more foolproof. Is there a consensus about which option is considered best practice?

    Thanks again,

    -- Tito

    On Jul 4, 2012, at 4:43 PM, Fritz Anderson <fritza...> wrote:

    > On 4 Jul 2012, at 6:30 PM, Tito Ciuro wrote:
    >
    >> void blockStep(fooBlock firstBlock, ...)
    >> {
    >> va_list args;
    >> va_start(args, firstBlock);
    >> id result = nil;
    >>
    >> do {
    >> result = firstBlock(result, nil);
    >> NSLog(@"%@", result);
    >> } while (nil != (firstBlock = va_arg(args, fooBlock)));
    >>
    >> va_end(args);
    >> }
    >>
    >> The output looks like this:
    >>
    >>> 2012-07-04 16:18:40.000 BlockStep[12418:303] One!
    >>> 2012-07-04 16:18:56.533 BlockStep[12418:303] One!, Two!
    >>
    >> I've eliminated the crash by adding a nil sentinel in blockStep():
    >>
    >> blockStep (
    >> ^ (id result, NSError **error) {
    >> NSMutableString *value = [NSMutableString new];
    >> [value appendString:@"One!"];
    >> return value;
    >> },
    >> ^ (id result, NSError **error) {
    >> NSMutableString *value = [NSMutableString new];
    >> if (nil != result) {
    >> [value appendString:result];
    >> [value appendString:@", "];
    >> }
    >> [value appendString:@"Two!"];
    >> return value;
    >> },
    >> nil
    >> );
    >
    >>
    >> This allows it to work without crashing, but I'd like if possible to avoid having to place the sentinel. Any ideas?
    >
    > Not possible.
    >
    > Notionally, all parameters are passed to C functions in memory on a stack, which is unformatted and could contain anything. A function has no way of knowing how many parameters have been pushed onto the stack, or where the memory trails off into saved processor state and the like, or the types, or the amounts of memory they subtend….
    >
    > Most variadic functions require sentinels (usually NULL) to tell them to stop looking for parameters. The best-known exceptions are printf()-family functions, which know what to find on the stack because the format string tells them.
    >
    > — F
    >
    > --
    > Fritz Anderson
    > Xcode 4 Unleashed: Don't bring your bathroom copy into the kitchen — were you raised in a barn?
    > <http://x4u.manoverboard.org/>
    >
    >
    >
  • Trust your judgment. It is sound in that sending an explicit parameter count is error-prone, as witness the thousands of crashes that occur every day when developers underrun their format strings.

    — F

    On 4 Jul 2012, at 7:34 PM, Tito Ciuro wrote:

    > It makes total sense now. Out of the two options (NULL sentinel vs a number to indicate the number of args), I would choose NULL because out of the two, it's more foolproof. Is there a consensus about which option is considered best practice?
  • On Jul 4, 2012, at 7:34 PM, Tito Ciuro wrote:

    > It makes total sense now. Out of the two options (NULL sentinel vs a number to indicate the number of args), I would choose NULL because out of the two, it's more foolproof. Is there a consensus about which option is considered best practice?

    Neither method is much foolproof at all — both of them can easily be undermined by even low-level fools. However, using a NULL sentinel is what is more commonly done.

    Charles
  • On 4 Jul 2012, at 7:40 PM, Charles Srstka wrote:

    > Neither method is much foolproof at all — both of them can easily be undermined by even low-level fools.

    … as witness the dozens of times a year I crash because I forgot to terminate an +arrayWithObjects: call, or the like.

    — F
  • I think it's easier to place a NULL than having to keep track of the number of args. None of them is foolproof, but placing NULL is all it takes to make it work. One could plug the wrong number of args and boum! Forcing to keep the number in sync is an extra step.

    -- Tito

    On Jul 4, 2012, at 5:40 PM, Charles Srstka <cocoadev...> wrote:

    > On Jul 4, 2012, at 7:34 PM, Tito Ciuro wrote:
    >
    >> It makes total sense now. Out of the two options (NULL sentinel vs a number to indicate the number of args), I would choose NULL because out of the two, it's more foolproof. Is there a consensus about which option is considered best practice?
    >
    > Neither method is much foolproof at all — both of them can easily be undermined by even low-level fools. However, using a NULL sentinel is what is more commonly done.
    >
    > Charles
    >
  • I prefer the NULL sentinel too. You might like to check if
    __attribute__((sentinel(0,1))) added to your function header gets the
    compiler to warn you if you forget it, very useful that.

    > Hi Fritz and Jens,
    >
    > It makes total sense now. Out of the two options (NULL sentinel vs a
    > number to indicate the number of args), I would choose NULL because out of
    > the two, it's more foolproof. Is there a consensus about which option is
    > considered best practice?
    >
    > Thanks again,
    >
    > -- Tito
    >
    > On Jul 4, 2012, at 4:43 PM, Fritz Anderson <fritza...>
    > wrote:
    >
    >> On 4 Jul 2012, at 6:30 PM, Tito Ciuro wrote:
    >>
    >>> void blockStep(fooBlock firstBlock, ...)
    >>> {
    >>> va_list args;
    >>> va_start(args, firstBlock);
    >>> id result = nil;
    >>>
    >>> do {
    >>> result = firstBlock(result, nil);
    >>> NSLog(@"%@", result);
    >>> } while (nil != (firstBlock = va_arg(args, fooBlock)));
    >>>
    >>> va_end(args);
    >>> }
    >>>
    >>> The output looks like this:
    >>>
    >>>> 2012-07-04 16:18:40.000 BlockStep[12418:303] One!
    >>>> 2012-07-04 16:18:56.533 BlockStep[12418:303] One!, Two!
    >>>
    >>> I've eliminated the crash by adding a nil sentinel in blockStep():
    >>>
    >>> blockStep (
    >>> ^ (id result, NSError **error) {
    >>> NSMutableString *value = [NSMutableString new];
    >>> [value appendString:@"One!"];
    >>> return value;
    >>> },
    >>> ^ (id result, NSError **error) {
    >>> NSMutableString *value = [NSMutableString new];
    >>> if (nil != result) {
    >>> [value appendString:result];
    >>> [value appendString:@", "];
    >>> }
    >>> [value appendString:@"Two!"];
    >>> return value;
    >>> },
    >>> nil
    >>> );
    >>
    >>>
    >>> This allows it to work without crashing, but I'd like if possible to
    >>> avoid having to place the sentinel. Any ideas?
    >>
    >> Not possible.
    >>
    >> Notionally, all parameters are passed to C functions in memory on a
    >> stack, which is unformatted and could contain anything. A function has
    >> no way of knowing how many parameters have been pushed onto the stack,
    >> or where the memory trails off into saved processor state and the like,
    >> or the types, or the amounts of memory they subtend….
    >>
    >> Most variadic functions require sentinels (usually NULL) to tell them to
    >> stop looking for parameters. The best-known exceptions are
    >> printf()-family functions, which know what to find on the stack because
    >> the format string tells them.
    >>
    >> — F
    >>
    >> --
    >> Fritz Anderson
    >> Xcode 4 Unleashed: Don't bring your bathroom copy into the kitchen —
    >> were you raised in a barn?
    >> <http://x4u.manoverboard.org/>
    >>
    >>
    >>

  • For function you can write a macro version of your function which calls, you function and appends a NULL to the VA_ARG macro. I don't normally do this but I have done something similar where I wanted macros to add meta data to a class, that expanded out into compete class method implementations.

    On 05/07/2012, at 11:09 AM, <rols...> wrote:

    > I prefer the NULL sentinel too. You might like to check if
    > __attribute__((sentinel(0,1))) added to your function header gets the
    > compiler to warn you if you forget it, very useful that.
    >
    >> Hi Fritz and Jens,
    >>
    >> It makes total sense now. Out of the two options (NULL sentinel vs a
    >> number to indicate the number of args), I would choose NULL because out of
    >> the two, it's more foolproof. Is there a consensus about which option is
    >> considered best practice?
    >>
    >> Thanks again,
    >>
    >> -- Tito
    >>
    >> On Jul 4, 2012, at 4:43 PM, Fritz Anderson <fritza...>
    >> wrote:
    >>
    >>> On 4 Jul 2012, at 6:30 PM, Tito Ciuro wrote:
    >>>
    >>>> void blockStep(fooBlock firstBlock, ...)
    >>>> {
    >>>> va_list args;
    >>>> va_start(args, firstBlock);
    >>>> id result = nil;
    >>>>
    >>>> do {
    >>>> result = firstBlock(result, nil);
    >>>> NSLog(@"%@", result);
    >>>> } while (nil != (firstBlock = va_arg(args, fooBlock)));
    >>>>
    >>>> va_end(args);
    >>>> }
    >>>>
    >>>> The output looks like this:
    >>>>
    >>>>> 2012-07-04 16:18:40.000 BlockStep[12418:303] One!
    >>>>> 2012-07-04 16:18:56.533 BlockStep[12418:303] One!, Two!
    >>>>
    >>>> I've eliminated the crash by adding a nil sentinel in blockStep():
    >>>>
    >>>> blockStep (
    >>>> ^ (id result, NSError **error) {
    >>>> NSMutableString *value = [NSMutableString new];
    >>>> [value appendString:@"One!"];
    >>>> return value;
    >>>> },
    >>>> ^ (id result, NSError **error) {
    >>>> NSMutableString *value = [NSMutableString new];
    >>>> if (nil != result) {
    >>>> [value appendString:result];
    >>>> [value appendString:@", "];
    >>>> }
    >>>> [value appendString:@"Two!"];
    >>>> return value;
    >>>> },
    >>>> nil
    >>>> );
    >>>
    >>>>
    >>>> This allows it to work without crashing, but I'd like if possible to
    >>>> avoid having to place the sentinel. Any ideas?
    >>>
    >>> Not possible.
    >>>
    >>> Notionally, all parameters are passed to C functions in memory on a
    >>> stack, which is unformatted and could contain anything. A function has
    >>> no way of knowing how many parameters have been pushed onto the stack,
    >>> or where the memory trails off into saved processor state and the like,
    >>> or the types, or the amounts of memory they subtend….
    >>>
    >>> Most variadic functions require sentinels (usually NULL) to tell them to
    >>> stop looking for parameters. The best-known exceptions are
    >>> printf()-family functions, which know what to find on the stack because
    >>> the format string tells them.
    >>>
    >>> — F
    >>>
    >>> --
    >>> Fritz Anderson
    >>> Xcode 4 Unleashed: Don't bring your bathroom copy into the kitchen —
    >>> were you raised in a barn?
    >>> <http://x4u.manoverboard.org/>
    >>>
    >>>
    >>>


  • On Jul 4, 2012, at 5:44 PM, Fritz Anderson <fritza...> wrote:
    > On 4 Jul 2012, at 7:40 PM, Charles Srstka wrote:
    >> Neither method is much foolproof at all — both of them can easily be undermined by even low-level fools.
    >
    > … as witness the dozens of times a year I crash because I forgot to terminate an +arrayWithObjects: call, or the like.

    The compiler will warn this. Turn on -Wformat if you don't have it already.

    --
    Greg Parker    <gparker...>    Runtime Wrangler
previous month july 2012 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Go to today