简体   繁体   English

使用goto在C语言中进行错误处理的奇怪行为

[英]Strange behavior using goto for error handling in C

I am developing a basic application in C, using the OP-TEE (TrustZone Secure OS) libraries. 我正在使用OP-TEE(TrustZone安全操作系统)库在C中开发基本应用程序。 I am running the code in QEMU. 我正在QEMU中运行代码。 Here is the code in which the strange behavior occurred: 这是发生奇怪行为的代码:

void prepare_rsa_operation(TEE_OperationHandle *handle, uint32_t alg, TEE_OperationMode mode, TEE_ObjectHandle key) {
    TEE_Result ret = TEE_SUCCESS;   
    TEE_ObjectInfo key_info;

    ret = TEE_GetObjectInfo1(key, &key_info);
    if (ret != TEE_SUCCESS) {
        EMSG("TEE_GetObjectInfo1: %#" PRIx32, ret);
        goto err;
    }

    ret = TEE_AllocateOperation(handle, alg, mode, key_info.keySize);
    if (ret != TEE_SUCCESS) {
        EMSG("Failed to alloc operation handle : 0x%x", ret);
        goto err;
    }
    DMSG("========== Operation allocated successfully. ==========");

    ret = TEE_SetOperationKey(*handle, key);
    if (ret != TEE_SUCCESS) {
        EMSG("Failed to set key : 0x%x", ret);
        goto err;
    }
    DMSG("========== Operation key already set. ==========");

err:
    TEE_FreeOperation(handle);
    return 1;
}

Problem that occurred: 发生的问题:
Both successful messages were being printed (for operation allocated and key setting), but the err label was being reached even though: the TEE_FreeOperation(handle); 两个成功的消息都被打印出来(用于分配操作和设置键),但是即使到达了err标签, TEE_FreeOperation(handle); should be written TEE_FreeOperation(*handle); 应该写为TEE_FreeOperation(*handle); . I fixed this and removed the return , since my function returns void . 我修复了此问题并删除了return ,因为我的函数返回void Now, the code is working fine, but, in my understanding, the err label should be reached only if one of the conditional tests ( if 's) fail, since the goto command is just inside them. 现在,代码可以正常工作,但是据我所知,仅当条件测试之一( if )失败时才应到达err标签,因为goto命令就在其中。

Am I wrong with this understanding? 我的理解错了吗? Can anyone explain me why the err label was being reached even though no error occurred before? 谁能解释我为什么即使以前没有发生错误也到达err标签?

The err: label is reached either if you goto it or after executing DMSG("========== Operation key already set. =========="); err:达到标签或者如果goto ,或执行后DMSG("========== Operation key already set. =========="); . Meaning you get a clean-up no matter if the function was successful or not. 意味着无论该功能是否成功,您都可以进行清理。 That's the whole reason for using "on error goto" patterns to begin with. 这就是开始使用“ on error goto”模式的全部原因。

A more readable alternative to the goto, is to return upon error and leave the clean-up in an external wrapper function. goto的一种更具可读性的替代方法是在出错时返回,并将清理工作留在外部包装函数中。

There's no special logic that prevents code from progressing past a label. 没有特殊的逻辑可以阻止代码经过标签。

By convention, goto is typically used in C for this type of error handing but it doesn't have to be that way. 按照惯例, goto通常在C中用于这种类型的错误处理,但不一定非要这样。 Labels for goto can be freely placed anywhere in a function. goto标签可以自由放置在函数中的任何位置。 You could for example do this: 例如,您可以这样做:

void f()
{
    int i;
start:
    printf("i=%d\n", i);
    i++;
    if (i < 10) {
        goto start;
}

But please don't. 但是请不要。

A labeled statement doesn't get skipped over if it's reached through normal flow of execution. 如果通过正常的执行流程到达带标签的语句,则不会跳过该语句。 IOW, given code like IOW,给出类似的代码

if ( condition )
{
  goto err;
}

err:
  // error handling code

// regular code

If condition evaluates to false, the code following err still gets executed because it follows the if statement. 如果condition值为false,则err的代码仍将执行,因为它遵循if语句。 You can avoid it by using second label and goto : 您可以通过使用第二个标签和goto避免它:

if ( condition )
{
  goto err;
}
goto normal;

err:
  // error handling code

normal:
  // regular code

but figuring out a goto-less way to handle the problem is better. 但是想办法解决问题的方法就更好了。

Much like Case labels in a switch-case statement, a label will just fall through to the next instruction when reached through the normal flow of code. 就像switch-case语句中的Case标签一样,当通过正常的代码流到达标签时,标签将落入下一条指令。 It is simply a place that can be jumped to. 它只是一个可以跳到的地方。 This functionality is taken advantage of when doing clean-up after an error. 错误发生后进行清理时,可以利用此功能。 For example, if you're mallocing a bunch of things and an error occurs, you can jump to different sections of the clean up code depending on when you hit your error: 例如,如果您正在分配一堆东西而发生错误,则可以根据遇到错误的时间跳转到清理代码的不同部分:

int func(void) {
    int ret = -1;
    int *x = malloc(sizeof(*x));
    if (/* some error condition */) {
        goto CLEANUP1;
    }
    int *y = malloc(sizeof(*y));
    if (/* some error condition */) {
        goto CLEANUP2;
    }
    int *z = malloc(sizeof(*z));
    if (/* some error condition */) {
        goto CLEANUP3;
    }
    ret = 0;
    /* do whatever successful operations you want here */

    CLEANUP3:
    free(z);
    CLEANUP2:
    free(y);
    CLEANUP1:
    free(x);
    return ret;
}

So with the above snippet, with normal error-free execution, all of the malloc 'd variables get free d before leaving the function. 因此,使用上面的代码段以及正常的无错误执行,所有malloc变量在离开函数之前都会获得free d。 If there is an error after malloc ing x , you jump to the CLEANUP1 label and free x . 如果出现后一种错误malloc荷兰国际集团x ,你跳转到CLEANUP1标签和free x if there is an error after malloc ing z , then you've also malloc 'd x and y , so you jump to the CLEANUP3 label which will free z and then fall through to the other two free s. 如果是后一种错误malloc荷兰国际集团z ,那么你也malloc “d xy ,让你跳转到CLEANUP3标签,这将free z然后告吹其他两个free秒。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM