Errno clobbering during String to UnsafePointer<CChar> conversion

Is it possible for the automatic String to UnsafePointer<CChar> conversion to clobber errno when calling functions imported from C modules? Is it dependent on whether the String is native UTF8 or bridged UTF16?

Thanks.

I think it's possible that in some cases it could happen (e.g. materializing a utf8 c-string from a utf16 NSString.) If you need access to your errno after the call to the C function, I would recommend using the String.withUTF8 closure-taking function, and check errno within the closure.

3 Likes

I would not rule it out (e.g. it could be reset in malloc/free done internally), and if that's happening in your case you may fix the issue at the C side – make a wrapper that converts errno to something better like the return value:

int foo(const char* param) { ... } // errno is set appropriately after the call

int fooWrapper(const char* param) {
    foo(param);
    return errno;
}

If obj-c is tolerable you may even make the call returning error in the standard obj-c way (it will look throwing when used from swift):

// typing in a web window so could contain typos
@implementation SomeClass
...
-(BOOL)fooWrapper:(NSString*)param error:(NSError**)error {
    int result = foo(param.UTF8String);
    if (result != 0) { // or whatever the check should be here
        *error = [NSError errorWithDomain: @"yourDomain" code:errno userInfo: nil];
        return false;
    }
    return true;
}
...
@end

Edit: corrected the obj-c code above: the obj-c NSError to swift throw conversion works only with obj-c methods, not with standalone functions.

2 Likes

On the Objective-C side, I write it this way:

int result = foo(…);
if ((result < 0) && (error != NULL)) { here
    int err = errno;
    *error = [NSError errorWithDomain: @"yourDomain" code:err userInfo: nil];
    return false;
}

This involves three changes:

  • I default to a < 0 test because Unix APIs are weird! But, as you said, this will vary based on the specific API you’re calling.

  • You have to check for error being NULL because Objective-C clients might not care about the exact error.

  • Sample errno before you start constructing the NSError, because C-based languages can evaluate parameters in any order and it’s possible that evaluating one of the other parameters might clobber errno.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

3 Likes

Yep. I'd modify your version a little bit further to return false/NO in case of error even if nil is passed in the error parameter.

You are right, that totally escaped me.

BTW, malloc only sets errno on error:

errno = 123
malloc(1) // ok
print(errno) // 123
print(errno) // still 123 (errno is not changed so far)
malloc(-1) // fail
print(errno) // 12

This is in accordance with the docs: "If there is an error, they return a NULL pointer and set errno to ENOMEM".

1 Like

Thanks for your advice!

Also, what about inout T to UnsafeMutablePointer<T> conversion? Is it possible for this to clobber errno, specifically when T is a POD type?

No.

2 Likes