Erkundung der Codequalität des Zephyr-Betriebssystems

PVS-Studio und Zephyr

Wir haben kürzlich gesagt, dass der PVS-Studio Code Analyzer mit der Integration in PlatformIO begonnen hat. Natürlich hat das PVS-Studio-Entwicklungsteam mit dem PlatformIO-Team kommuniziert und aus Gründen des Interesses vorgeschlagen, den Code des Zephyr-Echtzeitbetriebssystems zu überprüfen. Warum nicht, dachten wir, und hier ist ein Artikel über eine solche Studie.

PlatformIO


Bevor ich mit dem Hauptteil des Artikels beginne, möchte ich das PlatformIO- Projekt Entwicklern eingebetteter Systeme empfehlen , die ihnen das Leben erleichtern können. Es ist ein plattformübergreifendes Programmierwerkzeug für Mikrocontroller. Der Kern von PlatformIO ist ein Befehlszeilentool. Es wird jedoch empfohlen, es als Plug-In für Visual Studio Code zu verwenden. Eine große Anzahl moderner Chips und darauf basierender Motherboards wird unterstützt. Es kann automatisch geeignete Montagesysteme herunterladen und die Site verfügt über eine große Sammlung von Bibliotheken zur Verwaltung von elektronischen Plug-in-Komponenten.

PVS-Studio


PVS-Studio ist in der Welt der eingebetteten Systeme noch wenig bekannt. Für alle Fälle werde ich eine Einführung für neue Leser geben, die mit diesem Tool noch nicht vertraut sind. Unsere regelmäßigen Leser können direkt zum nächsten Abschnitt springen.

PVS-Studio ist ein statischer Code-Analysator, mit dem Fehler und potenzielle Schwachstellen im Code von Programmen erkannt werden können, die in C, C ++, C # und Java geschrieben wurden. Wenn wir nur über C und C ++ sprechen, werden die folgenden Compiler unterstützt:

  • Windows Visual Studio 2010-2019 C, C ++, C ++ / CLI, C ++ / CX (WinRT)
  • Windows IAR Embedded Workbench, C / C ++ - Compiler für ARM C, C ++
  • Windows QNX Momentics, QCC C, C ++
  • Windows / Linux Keil µVision, DS-MDK, ARM-Compiler 5/6 C, C ++
  • Windows / Linux Code Composer Studio von Texas Instruments, Tools zur ARM-Codegenerierung C, C ++
  • Windows/Linux/macOS. GNU Arm Embedded Toolchain, Arm Embedded GCC compiler, C, C++
  • Windows/Linux/macOS. Clang C, C++
  • Linux/macOS. GCC C, C++
  • Windows. MinGW C, C++

Der Analysator verfügt über ein eigenes Warnklassifizierungssystem. Bei Bedarf können Sie jedoch die Anzeige von Warnungen gemäß den Codierungsstandards CWE , SEI CERT , MISRA aktivieren .

Sie können PVS-Studio auch in einem großen Legacy-Projekt schnell und regelmäßig verwenden. Hierfür ist ein spezieller Mechanismus zur Massenunterdrückung von Warnungen vorgesehen . Alle aktuellen Warnungen gelten als technische Schulden und sind ausgeblendet, sodass Sie sich auf Warnungen konzentrieren können, die nur für neuen oder geänderten Code gelten. Auf diese Weise kann das Team den Analysator sofort täglich für seine Arbeit verwenden, und Sie können von Zeit zu Zeit zur technischen Aufgabe zurückkehren und den Code verbessern.

Es gibt viele andere Szenarien für die Verwendung von PVS-Studio. Sie können es beispielsweise als Plugin für SonarQube verwenden. Die Integration mit Systemen wie Travis CI, CircleCI, GitLab CI / CD usw. ist möglich. Eine detailliertere Beschreibung von PVS-Studio würde den Rahmen dieses Artikels sprengen. Daher schlage ich vor, sich mit dem Artikel vertraut zu machen, der viele nützliche Links enthält und viele Fragen beantwortet: " Gründe, den statischen Code-Analysator PVS-Studio in den Entwicklungsprozess einzuführen ."

Zephyr


Bei der Integration von PVS-Studio in PlatformIO sprachen unsere Teams und wurden gebeten, sich ein Projekt aus der eingebetteten Welt anzusehen, nämlich Zephyr. Uns hat die Idee gefallen, die der Grund für das Schreiben dieses Artikels war.

Zephyr ist ein leichtes Echtzeitbetriebssystem, das für Geräte mit begrenzten Ressourcen verschiedener Architekturen entwickelt wurde. Der Code wird unter der Open Source Apache 2.0-Lizenz verteilt. Funktioniert auf den folgenden Plattformen: 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.

Einige Eigenschaften:

  • Einheitlicher Adressraum. Der spezifische Anwendungscode in Kombination mit dem benutzerdefinierten Kernel erstellt ein monolithisches Image, das auf dem Gerät ausgeführt wird.
  • . , .
  • . .
  • . . .
  • : , , , , .

Von den für uns interessanten Momenten ist Synopsys an der Entwicklung des Betriebssystems beteiligt . 2014 erwarb Synopsys Coverity, das den gleichnamigen statischen Code-Analysator produzierte.

Es ist nur natürlich, dass der Zephyr-Entwickler von Anfang an den Coverity- Analysator verwendet . Der Analysator ist Marktführer und dies könnte die Qualität des Betriebssystemcodes nur zum Besseren beeinflussen.

Zephyr Code Qualität


Meiner Meinung nach ist der Code des Zephyr-Betriebssystems Qualität. Folgendes gibt mir Grund zu der Annahme:

  • PVS-Studio 122 High 367 Medium. , , 560 C/C++ . . 7810 C/C++ 10075 . , . , , .
  • . «» , .
  • Das SourceMonitor- Dienstprogramm , das den Quellcode analysiert hatte, gab Statistiken an, dass 48% des Codes Kommentare sind. Dies ist eine Menge und zeigt meiner Erfahrung nach, dass die Qualität des Codes und seine Verständlichkeit für andere Entwickler von großer Bedeutung sind.
  • Bei der Entwicklung eines Projekts wird ein statischer Coverity-Code-Analysator verwendet. Aufgrund dieser Tatsache konnte sich der PVS-Studio-Analysator höchstwahrscheinlich, obwohl er Fehler im Projekt fand, nicht anschaulich zeigen, wie dies manchmal bei der Analyse anderer Projekte der Fall ist.

Auf dieser Grundlage glaube ich, dass die Autoren des Projekts Wert auf die Qualität und Zuverlässigkeit des Codes legen. Schauen wir uns nun einige Warnungen an, die vom PVS-Studio-Analysegerät (Version 7.06) ausgegeben wurden.

Halb falsche Warnungen


Der Projektcode ist aufgrund seiner niedrigen Ebene sehr spezifisch und mit viel bedingter Kompilierung (#ifdef) geschrieben. Dies erzeugt eine große Anzahl von Warnungen, die keinen echten Fehler anzeigen, aber nicht einfach als falsch bezeichnet werden können. Es wird am einfachsten sein, dies anhand einiger Beispiele zu verdeutlichen.

Ein Beispiel für eine "halb-falsche" Betätigung N1

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 Warnung: V560 Ein Teil des bedingten Ausdrucks ist immer falsch :! Fb . cfb.c 188

Wenn die Adresse einer statischen Variablen verwendet wird, wird immer ein Zeiger ungleich Null erhalten. Daher ist der Zeiger fb immer ungleich Null und seine Überprüfung ist nicht sinnvoll.

Es ist jedoch klar, dass dies überhaupt kein Fehler ist, sondern lediglich eine übermäßige Überprüfung, die keinen Schaden anrichtet. Darüber hinaus wird der Compiler beim Erstellen der Release-Version diese wegwerfen, sodass dies nicht einmal zu einer Verlangsamung führt.

Ein ähnlicher Fall fällt meines Wissens unter das Konzept des "halbfalken" Analysatorbetriebs. Formal ist der Analysator absolut richtig. Und es ist besser, die zusätzliche sinnlose Prüfung aus dem Code zu entfernen. All dies ist jedoch kleinlich und solche Warnungen sind im Rahmen des Artikels nicht einmal interessant zu berücksichtigen.

Ein Beispiel für eine "halb-falsche" Betätigung N2

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;
}

Warnung PVS-Studio: V560 Ein Teil des bedingten Ausdrucks ist immer wahr: x> = 10. hex.c 31

Der Analysator hat wieder formal Recht, wenn er behauptet, dass ein Teil der Bedingung immer wahr ist. Wenn die Variable x nicht kleiner als / gleich 9 ist, stellt sich heraus, dass sie immer größer als / gleich 10 ist. Und der Code kann vereinfacht werden:

} else if (x <= 15) {

Es ist wieder einmal klar, dass es hier keinen wirklich schädlichen Fehler gibt, und der zusätzliche Vergleich wurde nur für die Schönheit des Codes geschrieben.

Schauen wir uns nun ein komplexeres Beispiel für N3 an.

Lassen Sie uns zunächst sehen, wie das CHECKIF- Makro implementiert werden kann .

#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

Abhängig vom Kompilierungsmodus des Projekts kann die Prüfung entweder durchgeführt oder übersprungen werden. In unserem Fall wurde bei der Überprüfung des Codes mit PVS-Studio diese Makroimplementierung ausgewählt:

#define CHECKIF(expr) \
  if (expr)

Nun wollen wir sehen, wozu das führt.

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- Warnung : V547 [CWE-571] Der Ausdruck 'head! = NULL' ist immer wahr. queue.c 244

Der Analysator ist der Ansicht, dass die Überprüfung (head! = NULL) immer true ergibt. Und tatsächlich ist es so. Wenn der Kopf Zeiger NULL ist, dann würde die Funktion nicht mehr funktioniert aufgrund einer Kontrolle am Anfang der Funktion:

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

Denken Sie daran, dass das Makro hier wie folgt erweitert wird:

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

Der PVS-Studio-Analysator ist also aus seiner Sicht richtig und gibt eine korrekte Warnung aus. Diese Prüfung kann jedoch nicht gelöscht werden. Sie wird gebraucht. In einem anderen Szenario wird das Makro folgendermaßen geöffnet:

if (0) {
  return -EINVAL;
}

Anschließend muss der Zeiger erneut überprüft werden. Natürlich generiert der Analysator in dieser Version der Codekompilierung keine Warnung. Es gibt jedoch eine Warnung für die Debug-Version der Kompilierung.

Ich hoffe, dass den Lesern jetzt klar ist, woher die "halb-falschen" Warnungen kommen. Es ist jedoch nichts falsch mit ihnen. PVS-Studio Analyzer bietet verschiedene Mechanismen zur Unterdrückung von Fehlalarmen, die in der Dokumentation zu finden sind.

Fallwarnungen


Aber haben Sie doch etwas Interessantes gefunden? Es war ein Erfolg, und jetzt werden wir uns verschiedene Fehler ansehen. In diesem Fall möchte ich sofort zwei Punkte beachten:

  1. . : , , , Coverity. , PVS-Studio - , .
  2. . , «» . , . GitHub, PVS-Studio.

N1,

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 Warnung: V501 [CWE-571] Links und rechts vom Operator '&&' befinden sich identische Unterausdrücke: link.tx.cb && link.tx.cb pb_adv.c 377

Einer ist doppelt geprüft die gleiche Variable link.tx.cb . Anscheinend ist dies ein Tippfehler, und die zweite zu überprüfende Variable sollte link.tx.cb_data sein .

Fragment N2, das den Puffer überläuft

Betrachten Sie die Funktion net_hostname_get , die später verwendet wird.

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

In meinem Fall wurde bei der Vorverarbeitung die Option für den Zweig #else ausgewählt . Das heißt, in der vorverarbeiteten Datei wird die Funktion wie folgt implementiert:
static inline const char *net_hostname_get(void)
{
  return "zephyr";
}

Die Funktion gibt einen Zeiger auf ein Array von 7 Bytes zurück (wir berücksichtigen die Klemme Null am Ende der Zeile).

Betrachten Sie nun den Code, der zum Verlassen der Array-Grenze führt.

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

PVS-Studio Warnung: V512 [CWE-119] Ein Aufruf der Funktion 'memcpy' führt dazu, dass der Puffer 'net_hostname_get ()' außerhalb des Bereichs liegt. log_backend_net.c 114

Nach der Vorverarbeitung wird MAX_HOSTNAME_LEN wie folgt erweitert:

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

Dementsprechend tritt beim Kopieren von Daten ein Zeichenfolgenliteral außerhalb der Grenzen auf. Es ist schwer vorherzusagen, wie sich dies auf die Ausführung des Programms auswirkt, da dies zu undefiniertem Verhalten führt.

Fragment N3, möglicher Pufferüberlauf

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 Warnung: V512 [CWE-119] Ein Aufruf der Funktion 'snprintf' führt zum Überlaufen des Puffers 'full_name'. lwm2m_rw_json.c 826

Wenn wir Makrowerte ersetzen, sieht das Bild des Geschehens folgendermaßen aus:

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

Unter dem Puffer full_name, in dem die Zeichenfolge gebildet wird, werden nur 20 Bytes zugewiesen. In diesem Fall werden die Teile, aus denen der String gebildet wird, in Puffern mit einer Größe von 20 und 64 Bytes gespeichert. Darüber hinaus ist die Konstante 64, die an die snprintf- Funktion übergeben wurde und verhindern soll, dass das Array ins Ausland geht, offensichtlich zu groß!

Dieser Code führt nicht unbedingt zu einem Pufferüberlauf. Vielleicht immer Glück und Teilzeichenfolgen sind immer sehr klein. Im Allgemeinen ist dieser Code jedoch in keiner Weise vor Überlauf geschützt und enthält die klassische Sicherheitslücke CWE-119 .

Fragment N4, Ausdruck ist immer wahr

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- Warnung : V547 [CWE-570] Der Ausdruck 'len <0' ist immer falsch. Der vorzeichenlose Typwert ist niemals <0. Keys.c 312 Die

Variable len hat einen vorzeichenlosen Typ und kann daher nicht kleiner als 0 sein. Dementsprechend wird der Fehlerstatus in keiner Weise verarbeitet. An anderen Stellen der Typ int oder wird ssize_t verwendet das Ergebnis der speichern read_cb Funktion . Beispiel:


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

Hinweis. Mit der Funktion read_cb scheint alles schlecht zu sein . Tatsache ist, dass es so deklariert wird:

static u8_t read_cb(const struct bt_gatt_attr *attr, void *user_data)

Typ u8_t ist ein vorzeichenloses Zeichen .

Die Funktion gibt immer nur positive Zahlen vom Typ vorzeichenloses Zeichen zurück . Wenn Sie diesen Wert in eine vorzeichenbehaftete Variable vom Typ int oder ssize_t einfügen , ist der Wert immer positiv. Daher funktioniert die Überprüfung auf Fehlerstatus an anderen Stellen auch nicht. Aber ich habe mich nicht mit diesem Thema befasst.

Fragment N5, etwas sehr Seltsames

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 Warnung: V575 [CWE-628] Die Funktion 'memcpy' kopiert nicht die gesamte Zeichenfolge. Verwenden Sie die Funktion 'strcpy / strcpy_s', um das Terminal null beizubehalten. shell.c 427

Seltsamer Code

Jemand versuchte, ein Analogon der strdup- Funktion zu erstellen , aber es gelang ihm nicht.

Beginnen wir mit der Warnung des Analysators. Es wird berichtet, dass die memcpy- Funktion die Zeile kopiert, aber nicht das Terminal Null, und dies ist sehr verdächtig.

Es scheint, dass dieses Terminal 0 hier kopiert wird:

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

Aber nein! Hier ist ein Tippfehler, aufgrund dessen die Klemme Null in sich selbst kopiert wird! Beachten Sie, dass beim Schreiben in das Array mntpt und nicht in cpy_mntpt . Infolgedessen gibt die Funktion mntpt_prepare eine Zeichenfolge zurück, die mit einem Terminal Null unvollständig ist.

Tatsächlich wollte der Programmierer so schreiben:

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

Es ist jedoch immer noch nicht klar, warum es so kompliziert war! Dieser Code kann auf folgende Option vereinfacht werden:

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;
}

Fragment N6, der einen Zeiger vor der Validierung dereferenziert

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 Warnung: V595 [CWE-476] Der ' Pub' -Zeiger wurde verwendet, bevor er gegen nullptr verifiziert wurde. Überprüfen Sie die Zeilen: 708, 719. access.c 708

Ein sehr häufiges Fehlermuster. Zunächst wird der Zeiger dereferenziert, um ein Mitglied der Struktur zu initialisieren:

.send_rel = pub->send_rel,

Und erst dann wird geprüft, ob dieser Zeiger null sein kann.

Fragment N7-N9, Zeiger-Dereferenzierung vor der Validierung

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 Warnung: V595 [CWE-476] Der 'conn'-Zeiger wurde verwendet, bevor er gegen nullptr verifiziert wurde. Überprüfen Sie die Zeilen: 1071, 1073. tcp2.c 1071

Wie im vorherigen Fall. Eine Erklärung ist hier nicht erforderlich.

Zwei weitere solche Fehler sind hier zu sehen:

  • V595 [CWE-476] Der Zeiger 'context-> tcp' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Überprüfen Sie die Zeilen: 1512, 1518. tcp.c 1512
  • V595 [CWE-476] Der Zeiger 'fsm' wurde verwendet, bevor er gegen nullptr verifiziert wurde. Überprüfen Sie die Zeilen: 365, 382. fsm.c 365

Fragment N10, fehlerhafte Prüfung

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- Warnung : V547 [CWE-570] Ausdruck '(Ende - * p) <1' ​​ist immer falsch. x509_crt.c 635 Sehen

Sie sich die Bedingungen genau an:

  • * p <Ende
  • (Ende - * p) <1

Sie widersprechen sich.

Wenn (* p <Ende), dann gibt (Ende - * p) immer einen Wert von 1 oder mehr. Im Allgemeinen stimmt hier etwas nicht, aber ich weiß nicht, wie ich es richtig buchstabieren soll.

Fragment N11, nicht erreichbarer 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 Warnung: V547 [CWE-571] Der Ausdruck 'disp' ist immer wahr. lv_disp.c 148

Die Funktion wird beendet, wenn disp ein Nullzeiger ist. Im Gegenteil, dann wird überprüft, ob der Disp- Zeiger nicht null ist (und dies ist immer der Fall), und die Funktion beendet ihre Arbeit erneut.

Infolgedessen wird ein Teil des Codes in einer Funktion niemals die Kontrolle erhalten.

Fragment N12, seltsamer Rückgabewert

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 Warnung: V1001 Die Variable 'len' wird zugewiesen, aber am Ende der Funktion nicht verwendet. lwm2m_rw_oma_tlv.c 338

Die Funktion enthält zwei return-Anweisungen , die beide 0 zurückgeben. Es ist seltsam, dass die Funktion immer 0 zurückgibt. Es ist auch seltsam, dass die len- Variable nach der Zuweisung nicht mehr verwendet wird. Ich habe den großen Verdacht, dass es eigentlich so geschrieben werden sollte:

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

Fragment N13-N16, Synchronisationsfehler

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 Warnung: V1020 Die Funktion wurde beendet, ohne die Funktion 'k_mutex_unlock' aufzurufen. Überprüfen Sie die Zeilen: 620, 549. nvs.c 620

Es gibt eine Situation, in der eine Funktion ihre Arbeit beendet, ohne den Mutex zu entsperren. So wie ich es verstehe, wäre es richtig, so zu schreiben:

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;
}

Drei weitere solche Fehler:

  • V1020 Die Funktion wurde beendet, ohne die Funktion 'k_mutex_unlock' aufzurufen. Überprüfen Sie die Zeilen: 574, 549. nvs.c 574
  • V1020 Die Funktion wurde beendet, ohne die Funktion 'k_mutex_unlock' aufzurufen. Überprüfen Sie die Zeilen: 908, 890. net_context.c 908
  • V1020 Die Funktion wurde beendet, ohne die Funktion 'k_mutex_unlock' aufzurufen. Überprüfen Sie die Zeilen: 1194, 1189. shell.c 1194

Fazit


Ich hoffe, dass es Ihnen gefallen hat. Besuchen Sie unseren Blog , um mehr über Überprüfungen anderer Projekte und andere interessante Veröffentlichungen zu erfahren.

Verwenden Sie bei Ihrer Arbeit statische Analysegeräte, um die Anzahl der Fehler und potenziellen Schwachstellen bereits beim Schreiben von Code zu verringern. Besonders die frühzeitige Fehlererkennung ist für eingebettete Systeme relevant, da die Aktualisierung von Programmen häufig zeitaufwändig und teuer ist.

Ich empfehle außerdem, Ihre Projekte nicht mit dem PVS-Studio-Analysegerät zu verschieben und zu überprüfen. Lesen Sie den Artikel: Wie können Sie schnell interessante Warnungen anzeigen, die vom PVS-Studio Analyzer für C- und C ++ - Code generiert wurden?



Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Link zur Übersetzung: Andrey Karpov. Überprüfen des Code des Zephyr-Betriebssystems .

All Articles