博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
zephyr操作系统_检查Zephyr操作系统代码
阅读量:2518 次
发布时间:2019-05-11

本文共 28487 字,大约阅读时间需要 94 分钟。

zephyr操作系统

PVS-Studio and Zephyr

Some time ago we announced PVS-Studio's new feature that enabled it to integrate into PlatformIO. Naturally, our team kept in touch with the PlatformIO team while working on that feature, and they suggested that we check the real-time operating system Zephyr to see if we could find any interesting bugs in its code. We thought it was a good idea, and so here's this article about the check results.

不久前,我们宣布了PVS-Studio的新功能,使其能够集成到PlatformIO中。 自然,我们的团队在使用该功能时与PlatformIO团队保持联系,他们建议我们检查实时操作系统Zephyr,看看是否可以在其代码中找到任何有趣的错误。 我们认为这是个好主意,因此本文是有关检查结果的文章。

PlatformIO (PlatformIO)

Before proceeding with the main topic of this article, I'd like to mention to the developers of embedded systems — it can make their life a bit easier. PlatformIO is a cross-platform tool for microcontroller programming. The core of PlatformIO is a command-line tool, however it is recommended to use it as a plugin for Visual Studio Code. It supports a large number of modern microchips, and boards based on them. It can automatically download suitable build systems. The site has a large collection of libraries for managing plug-in electronic components. There is support for several static code analyzers, including PVS-Studio.

在继续本文的主要主题之前,我想向嵌入式系统开发人员提及它可以使他们的生活更轻松一些。 PlatformIO是用于微控制器编程的跨平台工具。 PlatformIO的核心是命令行工具,但是建议将其用作Visual Studio Code的插件。 它支持大量的现代微芯片和基于它们的板。 它可以自动下载合适的构建系统。 该站点具有大量用于管理插入式电子组件的库。 支持多种静态代码分析器,包括PVS-Studio。

PVS工作室 (PVS-Studio)

PVS-Studio is not much known in the world of embedded systems yet, so here's a brief overview of our tool in case you haven't heard of it. Our regular readers may skip over to the next section.

PVS-Studio在嵌入式系统领域尚不为人所知,因此,如果您没有听说过它,那么这里简要介绍一下我们的工具。 我们的普通读者可以跳到下一部分。

is a static code analyzer that can detect bugs and potential vulnerabilities in the code of programs written in C, C++, C#, and Java. As for C and C++, the following compilers are supported: 是静态代码分析器,可以检测用C,C ++,C#和Java编写的程序的代码中的错误和潜在漏洞。 对于C和C ++,支持以下编译器:
  • Windows. Visual Studio 2010-2019 C, C++, C++/CLI, C++/CX (WinRT)

    视窗。 Visual Studio 2010-2019年C,C ++,C ++ / CLI,C ++ / CX(WinRT)
  • Windows. IAR Embedded Workbench, C/C++ Compiler for ARM C, C++

    视窗。 IAR嵌入式工作台,用于ARM C,C ++的C / C ++编译器
  • Windows. QNX Momentics, QCC C, C++

    视窗。 QNX Momentics,QCC C,C ++
  • Windows/Linux. Keil µVision, DS-MDK, ARM Compiler 5/6 C, C++

    Windows / Linux。 Keil µVision,DS-MDK,ARM编译器5/6 C,C ++
  • Windows/Linux. Texas Instruments Code Composer Studio, ARM Code Generation Tools C, C++

    Windows / Linux。 德州仪器Code Composer Studio,ARM代码生成工具C,C ++
  • Windows/Linux/macOS. GNU Arm Embedded Toolchain, Arm Embedded GCC compiler, C, C++

    Windows / Linux / macOS。 GNU Arm嵌入式工具链,Arm嵌入式GCC编译器,C,C ++
  • Windows/Linux/macOS. Clang C, C++

    Windows / Linux / macOS。 Clang C,C ++
  • Linux/macOS. GCC C, C++

    Linux / macOS。 GCC C,C ++
  • Windows. MinGW C, C++

    视窗。 MinGW C,C ++

The analyzer uses its own classification system, but you can also have warnings issued according to the coding standards , , and .

分析仪使用自己的分类系统,但是您也可以根据编码标准 , 和发出警告。

PVS-Studio can be quickly adopted and put to regular use even in a large legacy project. This is achieved thanks to a special mechanism of mass . It makes the analyzer treat all existing warnings as technical debt and hide them, thus allowing you to focus on the warnings produced only on newly written or modified code. This enables the team to start using the analyzer in their everyday work, getting back every now and then to address the technical debt and gradually eliminate it.

即使在大型遗留项目中,也可以快速采用PVS-Studio并使其正常使用。 这要归功于特殊的群众机制。 它使分析仪将所有现有警告视为技术欠债,并将其隐藏起来,从而使您可以将精力集中在仅在新编写或修改的代码上产生的警告。 这使团队可以开始在日常工作中使用分析仪,时不时找回解决技术问题的方法,并逐步消除它。

PVS-Studio allows many other use scenarios. For instance, you can run it as a plugin for SonarQube. It can also integrate with such systems as Travis CI, CircleCI, GitLab CI/CD, and so on. A detailed description of PVS-Studio is outside the scope of this article, so please refer to the following article, which offers many useful links and answers to many questions: "".

PVS-Studio允许许多其他使用场景。 例如,您可以将其作为SonarQube的插件运行。 它还可以与Travis CI,CircleCI,GitLab CI / CD等系统集成。 PVS-Studio的详细描述不在本文的讨论范围之内,因此,请参考以下文章,该文章提供了许多有用的链接和许多问题的答案:“ “。

和风 (Zephyr)

While working on , we kept in touch with the PlatformIO team, and they suggested that we check Zephyr, a project from the embedded software world. We liked the idea, and that's how this article appeared.

在致力于将 ,我们与PlatformIO团队保持联系,他们建议我们检查来自嵌入式软件世界的项目Zephyr。 我们喜欢这个主意,这就是这篇文章的样子。

is a small real-time operating system for connected, resource-constrained and embedded devices (with an emphasis on microcontrollers) supporting multiple architectures and released under the Apache License 2.0. Supported platforms: ARM (Cortex-M0, Cortex-M3, Cortex-M4, Cortex-M23, Cortex-M33, Cortex-R4, Cortex-R5, Cortex-A53), x86, x86-64, ARC, RISC-V, Nios II, Xtensa. 是一个小型实时操作系统,用于支持多种架构的连接的,资源受限的嵌入式设备(重点是微控制器),并根据Apache License 2.0发布。 支持的平台:ARM(Cortex-M0,Cortex-M3,Cortex-M4,Cortex-M23,Cortex-M33,Cortex-R4,Cortex-R5,Cortex-A53),x86,x86-64,ARC,RISC-V, Nios II,Xtensa。

Here are some of its distinguishing features:

以下是其一些特色:

  • Single address space. Combines application-specific code with a custom kernel to create a monolithic image that gets loaded and executed on a system's hardware.

    单个地址空间。 将特定于应用程序的代码与自定义内核结合在一起,以创建一个整体映像,该映像将在系统的硬件上加载并执行。
  • Highly configurable / Modular for flexibility. Allows an application to incorporate only the capabilities it needs as it needs them, and to specify their quantity and size.

    高度可配置的/模块化的灵活性。 允许应用程序根据需要仅合并所需的功能,并指定其数量和大小。
  • Compile-time resource definition. Reduces code size and increases performance for resource-limited systems.

    编译时资源定义。 减少代码大小并提高资源受限系统的性能。
  • Minimal error checking. The same as the previous one, with complete debugging information provided during testing if needed.

    最小的错误检查。 与上一个相同,并在测试过程中根据需要提供了完整的调试信息。
  • A number of services for development: multi-threading, interrupt, inter-thread synchronization, memory allocation, power management, and many other services.

    许多开发服务:多线程,中断,线程间同步,内存分配,电源管理和许多其他服务。

One of the curious things about Zephyr is that is involved into its development. In 2014, Synopsys bought Coverity, the creator of a static analyzer of the same name.

关于Zephyr的一件奇怪的事情是参与了其开发。 2014年,Synopsys收购了Coverity,这是同名静态分析仪的创建者。

Hence it's only natural that Zephyr is being checked with from the very beginning. This tool is a leader among analyzers, which helped guarantee the high quality of Zephyr's source code.

因此,从一开始就对Zephyr进行检查是很自然的。 该工具是分析仪中的佼佼者,有助于保证Zephyr源代码的高质量。

和风代码的质量 (The quality of Zephyr's code)

If you ask me, Zephyr is a high-quality project. These are the reasons why I think so:

如果您问我,Zephyr是一个高质量的项目。 这些是我这么认为的原因:

  • PVS-Studio produced 122 general-purpose warnings of the High level and 367 warnings of the Medium level. That's not much, considering the total number of C/C++ files checked – 560. The kernel is checked through samples checking. The total estimate I got for the project was 7810 C/C++ files and 10075 header files, which means the check covered only part of the project. But then again, I didn't aim at checking the entire code base, and the number of warnings I got still corresponds to a low warning density.

    PVS-Studio生成了122个高级通用警报和367个中级警报。 考虑到已检查的C / C ++文件总数– 560,这不是很多。通过示例检查来检查内核。 我对该项目的总估算是7810个C / C ++文件和10075个头文件,这意味着检查仅涵盖了项目的一部分。 但是话又说回来,我的目的不是检查整个代码库,而我得到的警告数量仍然对应于低警告密度。
  • Many of the warnings turned out to be false positives or «semi-false positives». What I mean by the latter is explained below.

    许多警告被证明是误报或“半误报”。 我对后者的解释如下。
  • I used the utility to scan Zephyr's source code, and according to its statistics, comments make 48% of the code. That's quite a bit, and, as my practice proves, it means the developers really care about their code's quality and readability.

    我使用实用程序扫描Zephyr的源代码,根据其统计数据,注释占了代码的48%。 这相当多,而且正如我的实践所证明的,这意味着开发人员真的很在乎其代码的质量和可读性。

  • The project is checked with the Coverity static analyzer. This must explain why PVS-Studio – while having found some bugs – still hasn't performed as impressively as it sometimes does when analyzing other projects.

    使用Coverity静态分析器检查项目。 这必须可以解释为什么PVS-Studio尽管发现了一些错误,但是在分析其他项目时却没有有时表现得那么出色。

Taking all this into account, I conclude that the project's authors care about their code's quality and reliability. Now let's look at some of the warnings issued by the PVS-Studio analyzer (version 7.06).

考虑到所有这些因素,我得出的结论是,该项目的作者在乎其代码的质量和可靠性。 现在,让我们看一下PVS-Studio分析仪(版本7.06)发出的一些警告。

«半假»警告 («Semi-false» warnings)

Since the project's code deals with low-level functionality, it's written in a specific way and uses a lot of conditional compilation (#ifdef). This leads to a large number of warnings that don't point at genuine bugs yet can't be viewed as outright false. This can be best explained with examples.

由于该项目的代码处理低级功能,因此它是以特定方式编写的,并使用了很多条件编译(#ifdef)。 这导致大量警告不指向真正的错误,但不能被视为完全错误。 最好用示例来解释。

“半假”肯定:示例1 («Semi-false» positives: example 1)

static struct char_framebuffer char_fb;int cfb_framebuffer_invert(struct device *dev){  struct char_framebuffer *fb = &char_fb;  if (!fb || !fb->buf) {    return -1;  }  fb->inverted = !fb->inverted;  return 0;}

PVS-Studio diagnostic message: A part of conditional expression is always false: !fb. cfb.c 188

PVS-Studio诊断消息: 条件表达式的一部分始终为false:!fb。 cfb.c 188

Obtaining the address of a static variable always yields a non-null pointer, so the fb pointer is never equal to zero and, therefore, the check is not needed.

获取静态变量的地址始终会生成非空指针,因此fb指针永远不会等于零,因此不需要进行检查。

Yet it's obviously not a bug but simply a redundant, harmless check. Besides, the compiler will optimize it away when building a Release version, so this check wouldn't even cause any slowdown.

但这显然不是错误,而只是多余的,无害的检查。 此外,编译器将在构建发行版时对其进行优化,因此此检查甚至不会导致任何速度下降。

That's what I call «semi-false» positives. Technically, the analyzer is totally correct in pointing out that redundant check, and it's better to remove it. On the other hand, minor issues like that are too petty and plain even to mention here.

这就是我所说的“半假”肯定。 从技术上讲,分析仪指出冗余检查是完全正确的,最好将其删除。 另一方面,诸如此类的小问题太小而太简单了,甚至在这里也没有提及。

“半假”肯定:示例2 («Semi-false» positives: example 2)

int hex2char(u8_t x, char *c){  if (x <= 9) {    *c = x + '0';  } else if (x >= 10 && x <= 15) {    *c = x - 10 + 'a';  } else {    return -EINVAL;  }  return 0;}

PVS-Studio diagnostic message: A part of conditional expression is always true: x >= 10. hex.c 31

PVS-Studio诊断消息: 条件表达式的一部分始终为true:x> =10。hex.c 31

Again, the analyzer is technically correct by pointing out an always-true conditional subexpression. If the x variable is not less than or equal to 9, then it naturally will be always greater than or equal to 10. So the code can be simplified:

同样,分析器通过指出始终为真的条件子表达式在技术上是正确的。 如果x变量不小于或等于9,则它自然总是大于或等于10。因此可以简化代码:

} else if (x <= 15) {

Again, the redundant check is not a true, harmful bug but just a decoration.

同样,多余的检查不是真正的有害错误,而只是装饰。

“半假”肯定:示例3,更复杂的情况 ( «Semi-false» positives: example 3, more complicated case)

First let's look at the possible implementations of the CHECKIF macro:

首先,让我们看一下CHECKIF宏的可能实现:

#if defined(CONFIG_ASSERT_ON_ERRORS)#define CHECKIF(expr) \  __ASSERT_NO_MSG(!(expr));   \  if (0)#elif defined(CONFIG_NO_RUNTIME_CHECKS)#define CHECKIF(...) \  if (0)#else#define CHECKIF(expr) \  if (expr)#endif

Depending on the compilation mode, the check will be either executed or skipped. In our case, when analyzing the project with PVS-Studio, the following implementation was selected:

根据编译模式,将执行或跳过检查。 在我们的案例中,当使用PVS-Studio分析项目时,选择了以下实现:

#define CHECKIF(expr) \  if (expr)

Let's see where we get from here.

让我们看看从这里到哪里。

int k_queue_append_list(struct k_queue *queue, void *head, void *tail){  CHECKIF(head == NULL || tail == NULL) {    return -EINVAL;  }  k_spinlock_key_t key = k_spin_lock(&queue->lock);  struct k_thread *thread = NULL;  if (head != NULL) {    thread = z_unpend_first_thread(&queue->wait_q);  }  ....}

PVS-Studio diagnostic message: [CWE-571] Expression 'head != NULL' is always true. queue.c 244

PVS-Studio诊断消息: [CWE-571]表达式'head!= NULL'始终为true。 队列c 244

The analyzer believes the (head != NULL) check is always true. That's correct. If the head pointer is equal to NULL, the check at the beginning of the function will have it exit sooner:

分析仪认为(head!= NULL)检查始终为真。 没错 如果指针等于NULL,则函数开始处的检查将使它更快退出:

CHECKIF(head == NULL || tail == NULL) {  return -EINVAL;}

As a reminder, this is what the macro expands into in this implementation:

提醒一下,这是宏在此实现中的扩展:

if (head == NULL || tail == NULL) {  return -EINVAL;}

So again, PVS-Studio is technically right and its warning is to the point. But you can't just remove the check because it's still needed. Should the other scenario be selected, the macro would expand as follows:

同样,PVS-Studio在技术上是正确的,其警告已到了重点。 但是您不能只删除支票,因为它仍然需要。 如果选择了其他情况,则宏将如下扩展:

if (0) {  return -EINVAL;}

Now you want the redundant check. Sure, the analyzer wouldn't produce the warning in that case, but it does in this one, where we deal with the Debug version.

现在,您需要冗余检查。 当然,在这种情况下,分析器不会发出警告,但是在这种情况下,我们会处理Debug版本。

I hope it's clear now where «semi-false» positives come from. They aren't a problem, though. PVS-Studio provides a handful of false warning suppression mechanisms, which are described in detail in the documentation.

我希望现在弄清楚“半假”肯定词来自何处。 不过,这不是问题。 PVS-Studio提供了一些错误警告抑制机制,在文档中对其进行了详细说明。

相关警告 (Relevant warnings)

Were there any interesting warnings then? Yes, there were, and we are going to take a look at some bugs of different types. But I'd like to make two statements first:

那时有没有有趣的警告? 是的,有,我们将研究一些不同类型的错误。 但我想先发表两个声明:

  1. Static analysis is not about one-time checks like this. The correct use strategy is to regularly run the analyzer on the project, which is actually exactly how Coverity is used in the development of Zephyr. And that's how adopting PVS-Studio or any other analyzer allows you to detect even more bugs and thus make them cheaper to fix at earlier stages.

    静态分析不是像这样的一次性检查。 正确的使用策略是定期在项目上运行分析仪,这实际上正是Zephyr开发中使用Coverity的方式。 这就是采用PVS-Studio或任何其他分析器的方式,它使您可以检测更多的错误,从而使在早期进行修复时更便宜。
  2. While writing this article, I didn't aim at finding as many bugs as possible and I could have well missed many of the bugs or erroneously discarded them as «semi-false» positives. If Zephyr's authors are reading this, I recommend that you check the project and study the analysis report on your own. Since the project is open-source and available on GitHub, you can use a option.

    在撰写本文时,我的目的并不是要找到尽可能多的错误,而我很可能会错过许多错误,或者将它们错误地误认为是“半假”。 如果Zephyr的作者正在阅读本文,建议您检查项目并自己研究分析报告。 由于该项目是开源的,并且在GitHub上可用,因此您可以使用选项。

片段1,错字 (Fragment 1, typo)

static void gen_prov_ack(struct prov_rx *rx, struct net_buf_simple *buf){  ....  if (link.tx.cb && link.tx.cb) {    link.tx.cb(0, link.tx.cb_data);  }  ....}

PVS-Studio diagnostic message: [CWE-571] There are identical sub-expressions to the left and to the right of the '&&' operator: link.tx.cb && link.tx.cb pb_adv.c 377

PVS-Studio诊断消息: [CWE-571]'&&'运算符的左侧和右侧具有相同的子表达式:link.tx.cb && link.tx.cb pb_adv.c 377

The link.tx.cb variable is checked twice. This must be a typo, with link.tx.cb_data being the second variable to be checked instead.

link.tx.cb变量被检查两次。 这必须是一个错字,其中link.tx.cb_data是要检查的第二个变量。

片段2,缓冲区溢出 (Fragment 2, buffer overflow)

Let's take a look at the net_hostname_get function, which will be used further.

让我们看一下net_hostname_get函数,将进一步使用它。

#if defined(CONFIG_NET_HOSTNAME_ENABLE)const char *net_hostname_get(void);#elsestatic inline const char *net_hostname_get(void){  return "zephyr";}#endif

In my case, the #else branch implementation was selected at the preprocessing stage, i.e. the preprocessed file will contain the following implementation of the function:

在我的情况下,在预处理阶段选择了#else分支实现,即,预处理文件将包含以下函数实现:

static inline const char *net_hostname_get(void){  return "zephyr";}

The function returns a pointer to an array of 7 bytes (including the terminating null character at the end of the string).

该函数返回一个指向7个字节的数组的指针(包括在字符串末尾的终止空字符)。

Now, here's the code where the overflow occurs.

现在,这里是发生溢出的代码。

static int do_net_init(void){  ....  (void)memcpy(hostname, net_hostname_get(), MAX_HOSTNAME_LEN);  ....}

PVS-Studio diagnostic message: [CWE-119] A call of the 'memcpy' function will lead to the 'net_hostname_get()' buffer becoming out of range. log_backend_net.c 114

PVS-Studio诊断消息: [CWE-119]调用'memcpy'函数将导致'net_hostname_get()'缓冲区超出范围。 log_backend_net.c 114

After the preprocessing, MAX_HOSTNAME_LEN expands as follows:

预处理后, MAX_HOSTNAME_LEN扩展如下:

(void)memcpy(hostname, net_hostname_get(),    sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx"));

Therefore, when copying the data, the program will end up accessing memory beyond the string literal's bounds. How exactly it's going to affect the execution is hard to tell because this is undefined behavior.

因此,复制数据时,程序最终将访问超出字符串常量范围的内存。 由于这是未定义的行为,因此很难确定它将如何影响执行。

片段3,潜在的缓冲区溢出 (Fragment 3, potential buffer overflow)

int do_write_op_json(struct lwm2m_message *msg){  u8_t value[TOKEN_BUF_LEN];  u8_t base_name[MAX_RESOURCE_LEN];  u8_t full_name[MAX_RESOURCE_LEN];  ....  /* combine base_name + name */  snprintf(full_name, TOKEN_BUF_LEN, "%s%s", base_name, value);  ....}

PVS-Studio diagnostic message: [CWE-119] A call of the 'snprintf' function will lead to overflow of the buffer 'full_name'. lwm2m_rw_json.c 826

PVS-Studio诊断消息: [CWE-119]调用'snprintf'函数将导致缓冲区'full_name'溢出。 lwm2m_rw_json.c 826

Substituting the macros' values leads us to the following:

替换宏的值会使我们得出以下结论:

u8_t value[64];u8_t base_name[20];u8_t full_name[20];....snprintf(full_name, 64, "%s%s", base_name, value);

Only 20 bytes are allocated for the full_name buffer, which is where the string is formed, while the parts that the string is formed from are stored in two buffers 20 and 64 bytes long. In addition, the constant 64 passed to the snprintf function and intended to prevent the overflow is obviously a bit too large!

仅20个字节分配给full_name缓冲区,这是字符串的形成位置,而字符串的组成部分则存储在两个20和64字节长的缓冲区中。 另外,传递给snprintf函数的用于防止溢出的常数64显然太大了!

This code won't necessarily end up with a buffer overflow. Perhaps the developers always get away with it because the substrings are always too short. But overall, this code has no protection against an overflow, which is a classic security weakness .

这段代码不一定会因缓冲区溢出而结束。 也许开发人员总是不喜欢它,因为子字符串总是太短。 但是总的来说,此代码无法防止溢出,这是经典的安全漏洞 。

片段4,表达始终为真 (Fragment 4, expression is always true)

static int keys_set(const char *name, size_t len_rd, settings_read_cb read_cb,                    void *cb_arg){  ....  size_t len;  ....  len = read_cb(cb_arg, val, sizeof(val));  if (len < 0) {    BT_ERR("Failed to read value (err %zu)", len);    return -EINVAL;  }  ....}

PVS-Studio diagnostic message: [CWE-570] Expression 'len < 0' is always false. Unsigned type value is never < 0. keys.c 312

PVS-Studio诊断消息: [CWE-570]表达式'len <0'始终为false。 无符号类型值永远不会小于0。keys.c 312

The len variable is unsigned, which means it can't be less than 0. Therefore, the error state isn't handled in any way. Elsewhere, the value returned by the read_cb function is stored in a variable of type int or ssize_t. For example:

len变量是无符号的,这意味着它不能小于0。因此,不会以任何方式处理错误状态。 在其他地方, read_cb函数返回的值存储在intssize_t类型的变量中。 例如:

static inline int mesh_x_set(....){ ssize_t len; len = read_cb(cb_arg, out, read_len); if (len < 0) { ....}

注意。 (Note.)

Actually, the

其实,

read_cb function doesn't look fine at all. Look at its declaration:
read_cb函数看起来一点也不好。 看一下它的声明:
static u8_t read_cb(const struct bt_gatt_attr *attr, void *user_data)

The type u8_t is actually unsigned char.

u8_t类型实际上是无符号字符。

The function always returns only positive values of type unsigned char. If we store such a value into a signed variable of type int or ssize_t, it will still be a positive value. It means error state checks don't work in other cases either. But I didn't dig too deep into this issue.

该函数始终仅返回unsigned char类型的正值。 如果我们将这样的值存储到类型为intssize_t的带符号变量中,它将仍然是正值。 这意味着错误状态检查在其他情况下也不起作用。 但是我并没有深入探讨这个问题。

片段5,有些奇怪 (Fragment 5, something weird)

static char *mntpt_prepare(char *mntpt){  char *cpy_mntpt;  cpy_mntpt = k_malloc(strlen(mntpt) + 1);  if (cpy_mntpt) {    ((u8_t *)mntpt)[strlen(mntpt)] = '\0';    memcpy(cpy_mntpt, mntpt, strlen(mntpt));  }  return cpy_mntpt;}

PVS-Studio diagnostic message: [CWE-628] The 'memcpy' function doesn't copy the whole string. Use 'strcpy / strcpy_s' function to preserve terminal null. shell.c 427

PVS-Studio诊断消息: [CWE-628]'memcpy'函数不会复制整个字符串。 使用'strcpy / strcpy_s'函数保留终端null。 shell.c 427

The developer was trying to make a function similar to strdup but failed.

开发人员试图制作类似于strdup的功能,但失败了。

The warning says the memcpy function copies a string but fails to copy the terminating null character, which is a very strange behavior.

警告说memcpy函数复制字符串,但是无法复制终止的空字符,这是非常奇怪的行为。

You may think the copying of the terminating null takes place in the following line:

您可能认为终止空值的复制发生在以下行中:

((u8_t *)mntpt)[strlen(mntpt)] = '\0';

But that's wrong! It's a typo that causes the terminating null to get copied into itself! Note that the target array is mntpt, not cpy_mntpt. As a result, the mntpt_prepare function returns a non-terminated string.

但这是错误的! 这是一个错字,会导致终止null被复制到自身中! 请注意,目标数组是mntpt ,而不是cpy_mntpt 。 结果, mntpt_prepare函数返回一个非终止的字符串。

This is what should be written instead:

这是应该写的:

((u8_t *)cpy_mntpt)[strlen(mntpt)] = '\0';

But I still can't see the reason for such a complicated implementation! This code can be reduced to the following:

但是我仍然看不到实现如此复杂的原因! 此代码可以简化为以下代码:

static char *mntpt_prepare(char *mntpt){  char *cpy_mntpt;  cpy_mntpt = k_malloc(strlen(mntpt) + 1);  if (cpy_mntpt) {    strcpy(cpy_mntpt, mntpt);  }  return cpy_mntpt;}

片段6,检查前指针取消引用 (Fragment 6, pointer dereferencing before check)

int bt_mesh_model_publish(struct bt_mesh_model *model){  ....  struct bt_mesh_model_pub *pub = model->pub;  ....  struct bt_mesh_msg_ctx ctx = {    .send_rel = pub->send_rel,  };  ....  if (!pub) {    return -ENOTSUP;  }  ....}

PVS-Studio diagnostic message: [CWE-476] The 'pub' pointer was utilized before it was verified against nullptr. Check lines: 708, 719. access.c 708

PVS-Studio诊断消息: [CWE-476]在针对nullptr对其进行验证之前,已使用了'pub'指针。 检查行:708、719。access.c 708

This is a bug pattern. The pointer is first dereferenced to initialize a struct member:

这是一个错误模式。 首先将指针取消引用以初始化struct成员:

.send_rel = pub->send_rel,

And only then is it checked for null.

然后才检查是否为空。

片段7-9,指针在检查前取消引用 (Fragments 7-9, pointer dereferencing before check)

int net_tcp_accept(struct net_context *context, net_tcp_accept_cb_t cb,                   void *user_data){  ....  struct tcp *conn = context->tcp;  ....  conn->accept_cb = cb;  if (!conn || conn->state != TCP_LISTEN) {    return -EINVAL;  }  ....}

PVS-Studio diagnostic message: [CWE-476] The 'conn' pointer was utilized before it was verified against nullptr. Check lines: 1071, 1073. tcp2.c 1071

PVS-Studio诊断消息: [CWE-476]在针对nullptr对其进行验证之前,已使用了'conn'指针。 检查行:1071,1073。tcp2.c 1071

This case is the same as the previous one. No comments needed.

这种情况与前一种情况相同。 无需评论。

Two more errors like that:

这样的另外两个错误:

  • V595 [CWE-476] The 'context->tcp' pointer was utilized before it was verified against nullptr. Check lines: 1512, 1518. tcp.c 1512

    V595 [CWE-476]在针对nullptr进行验证之前,已使用“ context-> tcp”指针。 检查行:1512、1518。tcp.c 1512
  • V595 [CWE-476] The 'fsm' pointer was utilized before it was verified against nullptr. Check lines: 365, 382. fsm.c 365

    V595 [CWE-476]在针对nullptr对其进行验证之前,已使用了'fsm'指针。 检查行:365、382。fsm.c 365

片段10,检查不正确 (Fragment 10, incorrect check)

static int x509_get_subject_alt_name( unsigned char **p,                                      const unsigned char *end,                                      mbedtls_x509_sequence *subject_alt_name){  ....    while( *p < end )    {        if( ( end - *p ) < 1 )            return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +                    MBEDTLS_ERR_ASN1_OUT_OF_DATA );    ....  }  ....}

PVS-Studio diagnostic message: [CWE-570] Expression '(end — * p) < 1' is always false. x509_crt.c 635

PVS-Studio诊断消息: [CWE-570]表达式'( * p)<1'始终为false。 x509_crt.c 635

Look closely at these conditions:

仔细观察以下情况:

  • *p < end

    * p <结束
  • (end — *p) < 1

    (结尾-* p)<1

They are mutually opposite.

他们是相互对立的。

If (*p < end), then (end — *p) will always yield the value 1 or larger. Something is wrong with this code, but I have no idea how it should be fixed.

如果(* p <end),则(end-* p)将始终产生1或更大的值。 该代码有问题,但是我不知道应该如何解决。

片段11,无法访问的代码 (Fragment 11, unreachable code)

uint32_t lv_disp_get_inactive_time(const lv_disp_t * disp){    if(!disp) disp = lv_disp_get_default();    if(!disp) {        LV_LOG_WARN("lv_disp_get_inactive_time: no display registered");        return 0;    }    if(disp) return lv_tick_elaps(disp->last_activity_time);    lv_disp_t * d;    uint32_t t = UINT32_MAX;    d          = lv_disp_get_next(NULL);    while(d) {        t = LV_MATH_MIN(t, lv_tick_elaps(d->last_activity_time));        d = lv_disp_get_next(d);    }    return t;}

PVS-Studio diagnostic message: [CWE-571] Expression 'disp' is always true. lv_disp.c 148

PVS-Studio诊断消息: [CWE-571]表达式“ disp”始终为真。 lv_disp.c 148

The function returns if disp is a null pointer. This is followed by an opposite check – whether the disp pointer is non-null (which is always true) – and the function returns all the same.

如果disp是空指针,则函数返回。 随后进行相反的检查– disp指针是否为非null(始终为true)–函数将返回相同的结果。

Because of this logic, part of the code in the function's body will never get control.

由于这种逻辑,函数主体中的部分代码将永远无法获得控制。

片段12,奇怪的返回值 (Fragment 12, strange return value)

static size_t put_end_tlv(struct lwm2m_output_context *out, u16_t mark_pos,        u8_t *writer_flags, u8_t writer_flag,        int tlv_type, int tlv_id){  struct tlv_out_formatter_data *fd;  struct oma_tlv tlv;  u32_t len = 0U;  fd = engine_get_out_user_data(out);  if (!fd) {    return 0;  }  *writer_flags &= ~writer_flag;  len = out->out_cpkt->offset - mark_pos;  /* use stored location */  fd->mark_pos = mark_pos;  /* set instance length */  tlv_setup(&tlv, tlv_type, tlv_id, len);  len = oma_tlv_put(&tlv, out, NULL, true) - tlv.length;  return 0;}

PVS-Studio diagnostic message: The 'len' variable is assigned but is not used by the end of the function. lwm2m_rw_oma_tlv.c 338

PVS-Studio诊断消息: 分配了“ len”变量,但在功能结束时未使用该变量。 lwm2m_rw_oma_tlv.c 338

The function has two return statements both of which return 0. It's strange for a function to return 0 in any case. And it's strange that the len variable is never used after it has been assigned a value. I strongly suspect that the programmer actually meant to write the following code:

该函数有两个return语句,它们都返回0。在任何情况下函数都返回0很奇怪。 奇怪的是,在给len变量赋了值之后就再也没有使用它了。 我强烈怀疑程序员实际上打算编写以下代码:

len = oma_tlv_put(&tlv, out, NULL, true) - tlv.length;  return len;}

片段13-16,同步错误 (Fragments 13-16, synchronization error)

static int nvs_startup(struct nvs_fs *fs){  ....  k_mutex_lock(&fs->nvs_lock, K_FOREVER);  ....  if (fs->ate_wra == fs->data_wra && last_ate.len) {    return -ESPIPE;  }  ....end:  k_mutex_unlock(&fs->nvs_lock);  return rc;}

PVS-Studio diagnostic message: The function exited without calling the 'k_mutex_unlock' function. Check lines: 620, 549. nvs.c 620

PVS-Studio诊断消息: 该函数已退出,而未调用'k_mutex_unlock'函数。 检查线:620、549。nvs.c 620

The function may return without unlocking the mutex. As far as I understand, the following should be written instead:

该功能可能会在未解锁互斥锁的情况下返回。 据我了解,应该改写以下内容:

static int nvs_startup(struct nvs_fs *fs){  ....  k_mutex_lock(&fs->nvs_lock, K_FOREVER);  ....  if (fs->ate_wra == fs->data_wra && last_ate.len) {    rc = -ESPIPE;    goto end;  }  ....end:  k_mutex_unlock(&fs->nvs_lock);  return rc;}

Three more bugs of this type:

此类型的其他三个错误:

  • V1020 The function exited without calling the 'k_mutex_unlock' function. Check lines: 574, 549. nvs.c 574

    V1020该函数退出而未调用'k_mutex_unlock'函数。 检查行:574,549。nvs.c 574
  • V1020 The function exited without calling the 'k_mutex_unlock' function. Check lines: 908, 890. net_context.c 908

    V1020该函数退出而未调用'k_mutex_unlock'函数。 检查行:908、890。net_context.c 908
  • V1020 The function exited without calling the 'k_mutex_unlock' function. Check lines: 1194, 1189. shell.c 1194

    V1020该函数退出而未调用'k_mutex_unlock'函数。 检查行:1194、1189。shell.c 1194

结论 (Conclusion)

I hope you enjoyed reading this article. Be sure to check our for more project checks and other interesting articles.

希望您喜欢阅读本文。 请务必查看我们的以获取更多项目检查和其他有趣的文章。

Use static analyzers to eliminate tons of bugs and potential vulnerabilities at the earlier coding stage. Early bug detection is especially crucial to embedded systems, where updates are expensive and time-consuming.

在早期的编码阶段,使用静态分析器可以消除大量的错误和潜在的漏洞。 早期错误检测对于嵌入式系统而言尤其重要,因为嵌入式系统的更新既昂贵又耗时。

I also encourage you to go and check your own projects with PVS-Studio. For detailed information about how to do that see the article "".

我也鼓励您去PVS-Studio检查自己的项目。 有关如何执行此操作的详细信息,请参阅文章“ ”。

翻译自:

zephyr操作系统

转载地址:http://ladwd.baihongyu.com/

你可能感兴趣的文章
Linux发送qq、网易邮件服务配置
查看>>
几道面试题
查看>>
【转】使用 WebGL 进行 3D 开发,第 1 部分: WebGL 简介
查看>>
js用正则表达式控制价格输入
查看>>
相对定位、绝对定位
查看>>
chromium浏览器开发系列第三篇:chromium源码目录结构
查看>>
java开发操作系统内核:由实模式进入保护模式之32位寻址
查看>>
第五讲:单例模式
查看>>
Python编程语言的起源
查看>>
Azure ARMTemplate模板,VM扩展命令
查看>>
使用Masstransit开发基于消息传递的分布式应用
查看>>
[CF808A] Lucky Year(规律)
查看>>
关于推送遇到的一些问题
查看>>
寒假作业3 抓老鼠啊~亏了还是赚了?
查看>>
Orcal Job创建实例
查看>>
Django
查看>>
vue踩坑记
查看>>
批量Excel数据导入Oracle数据库(引用 自 wuhuacong(伍华聪)的专栏)
查看>>
批处理sqlldr数据迁移--oracle txt导入导出(转)
查看>>
with revoked permission android.permission.CAMERA
查看>>