【问题标题】:How to ensure that a specific code is not run while a condition is not met如何确保在不满足条件时不运行特定代码
【发布时间】:2016-03-01 14:29:22
【问题描述】:

我在嵌入式系统上使用 C/CPP,在我的代码中,我有一些部分是从一些“无效”的内存中运行的,并且在满足某些条件之前不得调用。

为简单起见:
假设foo() 是这样的功能并且在g_isMemoryValid == false 时无效

除了 foo() 之外,无效内存部分还有几个其他函数,每个函数都来自所有模块的多个调用。

我的问题是找到我输入无效部分的所有实例并验证我没有错过任何电话!

如何确保运行foo() 的所有分支都满足g_isMemoryValid == true


解决方案应该输出:
跳转到代码(或指令地址)的行, 但即使它只会发出模块名称,
或者地狱...... [OK,NOT_OK] 会做:)

注意:即使只解决部分场景的解决方案也可以!


请注意,除了简单的if(g_isMemoryValid) 包装器之外,还有许多场景,例如调用链本身的调用函数在不满足条件时不被调用或分支的某些不同语法

if (g_isMemoryValid) 
    foo(); // should pass

if (!g_isMemoryValid)
    return; 
foo(); // should pass too

【问题讨论】:

  • 将它们包裹在一个互斥锁中,当g_isMemoryValid 变为真时该互斥锁解锁?
  • 您是否可以访问foo()的源代码,以便您可以修改函数本身?或者,您可以为foo() 实现包装函数吗?
  • #define CHECKED_FOO() if (g_isMemoryValid == true) foo();
  • @LPs 您应该将其发布为答案。
  • 我不明白您的问题,您知道如何检查条件以及仅在条件为真时如何调用函数,所以这不是问题。您是在要求最“优雅”的方式吗?那么“优雅”按谁呢?这是一个非常基于意见的问题。此外,按照某些人的建议使用宏似乎很诱人,但恕我直言,这不是一个“优雅”的解决方案,而且对正在发生的事情非常不透明。

标签: c++ c embedded verification


【解决方案1】:

至少在我看来,一个比宏更优雅的解决方案是使用函数指针:

#include <stdio.h>

// The real foo()
void foo()
{
    puts("OK");
}

// Handle 'invalid' memory (error message?)
void mem_invalid()
{
    puts("Not OK");
}

typedef void (*foo_t)();

foo_t foo_ptr = &mem_invalid;

void main(void)
{
    // Memory is 'invalid', calls error routine
    foo_ptr();
    ...

    // Memory became good somehow
    foo_ptr = &foo;
    ...

    // Call now succeeds
    foo_ptr();
}

这使用函数指针代替标志。

根据您的嵌入式系统,间接函数调用可能会产生一些开销,但除非它位于像内部循环这样的关键区域,否则它不应该成为问题。错误处理程序甚至不需要做任何事情 - 或者它可以像你喜欢的那样复杂。

【讨论】:

  • 嵌入式系统上一些面向安全的编码风格指南不允许使用函数指针。
  • @Olaf,你指的是什么编码风格?此后已被废除的 Misra C 规则 104 曾经说过“不得使用指向函数的非常量指针”,但我的理解是,这意味着“不得在运行时计算指向函数的指针”。风险来自计算地址时的错误,这不会在这里发生,因为地址在编译时是已知的,并且将被编码为文字。在这种情况下,它的安全性不亚于直接调用。
  • 这不仅仅是因为这个,还因为指针可能会被数据访问损坏。我同意这样的规则是有问题的,但这并不能改变它们存在的事实。不过,我不会为他们辩护。
  • @Olaf,如果您的意思是可以覆盖函数指针 - 可能是通过使用超出范围的数组下标或无效指针,是的,这是一种风险,但是如果您的代码包含这样的错误那么函数指针可能是您的问题中最少的(例如,损坏的内存位置可能很容易包含数据指针或数组下标变量,或者导致无限循环的循环计数器,或者......)。
  • 有趣的是,来自 NASA/JPL 的 J Holzmann 还写了一篇论文“10 的力量:开发安全关键代码的规则”,其中规则 9 规定不使用函数指针。但是,如果您阅读他的论文,其基本原理是函数指针隐藏了静态检查工具的可能递归,而不是对内存损坏的任何担忧。递归显然有资源耗尽的风险(例如堆栈空间),这比内存损坏更难检查。
【解决方案2】:

如果你不能修改foo的调用方式,你必须修改foo本身:

  • foo 函数重命名为inner_foo
  • 给自己写一个foo,它会做所有的检查,打印所有的消息,并且,只要一切正常,就调用自己inner_foo
  • 重新编译包含旧 foo 和新 foo 的模块,并重新链接所有调用 foo 的模块。

现在所有分支都将调用您自己的新 foo 函数。

【讨论】:

  • 这是迄今为止最干净、最直接的方法。对于它的价值,因为帖子也被标记为C++...如果内存无效,包装器可以throw
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多