Menjelajahi kualitas kode sistem operasi Zephyr

PVS-Studio dan Zephyr

Kami baru-baru ini mengatakan bahwa penganalisa kode PVS-Studio mulai berintegrasi dengan PlatformIO. Secara alami, tim pengembangan PVS-Studio berkomunikasi dengan tim PlatformIO dan mereka menyarankan, demi kepentingan, memeriksa kode sistem operasi waktu-nyata Zephyr. Kenapa tidak, kami pikir, dan di sini ada artikel tentang penelitian semacam itu.

PlatformIO


Sebelum memulai bagian utama artikel, saya ingin merekomendasikan proyek PlatformIO kepada pengembang sistem embedded , yang dapat membuat hidup mereka lebih mudah. Ini adalah alat pemrograman mikrokontroler lintas-platform. Inti dari PlatformIO adalah alat baris perintah, tetapi disarankan untuk menggunakannya sebagai plug-in untuk Visual Studio Code. Sejumlah besar chip dan motherboard modern yang didukungnya didukung. Itu dapat secara otomatis mengunduh sistem perakitan yang sesuai, dan situs ini memiliki banyak koleksi perpustakaan untuk mengelola komponen elektronik plug-in.

PVS-Studio


PVS-Studio masih sedikit dikenal di dunia sistem embedded, jadi untuk berjaga-jaga, saya akan membuat pengantar untuk pembaca baru yang belum terbiasa dengan alat ini. Pembaca reguler kami dapat langsung beralih ke bagian selanjutnya.

PVS-Studio adalah penganalisa kode statis yang memungkinkan mendeteksi kesalahan dan potensi kerentanan dalam kode program yang ditulis dalam C, C ++, C # dan Java. Jika kita hanya berbicara tentang C dan C ++, maka kompiler berikut ini didukung:

  • Windows Visual Studio 2010-2019 C, C ++, C ++ / CLI, C ++ / CX (WinRT)
  • Windows IAR Embedded Workbench, C / C ++ Compiler untuk ARM C, C ++
  • Windows QNX Momentics, QCC C, C ++
  • Windows / Linux Keil μVision, DS-MDK, ARM Compiler 5/6 C, C ++
  • Windows / Linux Studio Komposer Kode Texas Instruments, Alat Pembuatan Kode ARM 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++

Alat analisis memiliki sistem klasifikasi peringatan sendiri , tetapi jika perlu, Anda dapat mengaktifkan tampilan peringatan sesuai dengan standar pengkodean CWE , SEI CERT , MISRA .

Anda dapat dengan cepat mulai secara teratur menggunakan PVS-Studio bahkan dalam proyek warisan besar. Mekanisme khusus untuk penindasan massal peringatan diberikan untuk ini . Semua peringatan saat ini dianggap sebagai hutang teknis dan disembunyikan, yang memungkinkan Anda untuk fokus pada peringatan yang hanya berlaku untuk kode yang baru atau yang dimodifikasi. Ini memungkinkan tim untuk segera mulai menggunakan alat analisa setiap hari dalam pekerjaan mereka, dan Anda dapat kembali ke tugas teknis dari waktu ke waktu dan meningkatkan kode.

Ada banyak skenario lain untuk menggunakan PVS-Studio. Misalnya, Anda dapat menggunakannya sebagai plugin untuk SonarQube. Integrasi dengan sistem seperti Travis CI, CircleCI, GitLab CI / CD, dll. Adalah mungkin. Penjelasan lebih rinci tentang PVS-Studio berada di luar cakupan artikel ini. Oleh karena itu, saya mengusulkan untuk membiasakan diri dengan artikel tersebut, yang memiliki banyak tautan bermanfaat, dan yang menjawab banyak pertanyaan: " Alasan untuk memperkenalkan penganalisa kode statis PVS-Studio ke dalam proses pengembangan ."

Angin barat


Bekerja pada integrasi PVS-Studio ke dalam PlatformIO , tim kami berbicara dan diminta untuk memeriksa proyek dari dunia yang disematkan, yaitu Zephyr. Kami menyukai ide itu, yang merupakan alasan untuk menulis artikel ini.

Zephyr adalah sistem operasi real-time ringan yang dirancang untuk bekerja pada perangkat dengan sumber daya terbatas dari berbagai arsitektur. Kode didistribusikan di bawah lisensi open source Apache 2.0. Bekerja pada platform berikut: 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.

Beberapa fitur:

  • Ruang alamat terpadu. Kode aplikasi spesifik dalam kombinasi dengan kernel kustom membuat gambar monolitik yang dijalankan pada perangkat.
  • . , .
  • . .
  • . . .
  • : , , , , .

Dari momen-momen menarik bagi kami, Synopsys terlibat dalam pengembangan sistem operasi . Pada 2014, Synopsys mengakuisisi Coverity, yang menghasilkan penganalisa kode statis dengan nama yang sama.

Wajar jika sejak awal, pengembang Zephyr menggunakan penganalisa Coverity . Penganalisa adalah pemimpin pasar dan ini tidak bisa tidak mempengaruhi kualitas kode sistem operasi menjadi lebih baik.

Kualitas Kode Zephyr


Menurut pendapat saya, kode sistem operasi Zephyr adalah kualitas. Inilah yang memberi saya alasan untuk berpikir demikian:

  • PVS-Studio 122 High 367 Medium. , , 560 C/C++ . . 7810 C/C++ 10075 . , . , , .
  • . «» , .
  • Utilitas SourceMonitor , setelah menganalisis kode sumber, memberikan statistik bahwa 48% dari kode adalah komentar. Ini banyak dan dalam pengalaman saya menunjukkan perhatian yang tinggi terhadap kualitas kode, kelengkapannya untuk pengembang lain.
  • Saat mengembangkan proyek, penganalisa kode statis Coverity digunakan. Kemungkinan besar, karena fakta ini, penganalisa PVS-Studio, meskipun menemukan kesalahan dalam proyek, tidak dapat menunjukkan dirinya dengan jelas, seperti yang kadang-kadang terjadi ketika menganalisis proyek lain.

Berdasarkan ini, saya percaya bahwa penulis proyek peduli tentang kualitas dan keandalan kode. Mari sekarang kita lihat beberapa peringatan yang dikeluarkan oleh alat analisa PVS-Studio (versi 7.06).

Peringatan semi-salah


Kode proyek, karena tingkatannya yang rendah, ditulis dengan sangat spesifik dan dengan banyak kompilasi bersyarat (#ifdef). Ini menghasilkan sejumlah besar peringatan yang tidak menunjukkan kesalahan nyata, tetapi mereka tidak bisa disebut hanya salah. Akan lebih mudah untuk mengklarifikasi ini dengan beberapa contoh.

Contoh aktuasi “semi-salah” 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;
}

Peringatan PVS-Studio: V560 Bagian dari ekspresi kondisional selalu salah :! Fb . cfb.c 188

Saat mengambil alamat variabel statis, selalu ada pointer non-nol. Oleh karena itu, pointer fb selalu non-nol dan verifikasi tidak masuk akal.

Namun, jelas bahwa ini bukan kesalahan sama sekali, tetapi hanya pemeriksaan berlebihan, yang tidak membahayakan. Selain itu, ketika membangun versi Rilis, kompiler akan membuangnya, jadi ini bahkan tidak akan menyebabkan perlambatan.

Kasus serupa, dalam pemahaman saya, berada di bawah konsep operasi analisa "semi-false". Secara formal, alat analisa itu benar sekali. Dan lebih baik untuk menghapus cek tambahan yang tidak berguna dari kode. Namun, semua ini sepele dan peringatan semacam itu bahkan tidak menarik untuk dipertimbangkan dalam kerangka artikel.

Contoh aktuasi "semi-potong" dari 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;
}

Peringatan PVS-Studio: V560 Bagian dari ekspresi kondisional selalu benar: x> = 10. hex.c 31

Analiser lagi benar secara formal dalam menyatakan bahwa bagian dari kondisi selalu benar. Jika variabel x tidak kurang dari / sama dengan 9, maka ternyata selalu lebih besar dari / sama dengan 10. Dan kode dapat disederhanakan:

} else if (x <= 15) {

Sekali lagi, jelas bahwa tidak ada kesalahan berbahaya yang nyata di sini, dan perbandingan tambahan ditulis hanya untuk keindahan kode.

Sekarang mari kita lihat contoh N3 yang lebih kompleks.

Pertama, mari kita lihat bagaimana makro CHECKIF dapat diimplementasikan .

#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

Bergantung pada mode kompilasi proyek, pemeriksaan dapat dilakukan atau dilewati. Dalam kasus kami, ketika memeriksa kode menggunakan PVS-Studio, implementasi makro ini dipilih:

#define CHECKIF(expr) \
  if (expr)

Sekarang mari kita lihat apa yang menyebabkan hal ini.

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

Peringatan PVS-Studio: V547 [CWE-571] Ekspresi 'head! = NULL' selalu benar. queue.c 244

Penganalisa menganggap bahwa memeriksa (head! = NULL) selalu memberikan true. Dan memang benar. Jika penunjuk kepala NULL, maka fungsi akan berhenti bekerja karena centang di awal fungsi:

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

Ingatlah bahwa di sini makro diperluas sebagai berikut:

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

Jadi, analisa PVS-Studio tepat dari sudut pandangnya dan mengeluarkan peringatan yang benar. Namun, pemeriksaan ini tidak dapat dihapus. Dia dibutuhkan. Dalam skenario lain, makro akan terbuka seperti ini:

if (0) {
  return -EINVAL;
}

Dan kemudian memeriksa kembali pointer diperlukan. Tentu saja, penganalisa tidak akan menghasilkan peringatan dalam versi kompilasi kode ini. Namun, itu memberikan peringatan untuk versi debug kompilasi.

Saya harap sekarang jelas bagi pembaca dari mana peringatan "semi-salah" berasal. Namun, tidak ada yang salah dengan mereka. Alat analisis PVS-Studio menyediakan berbagai mekanisme untuk menekan peringatan palsu, yang dapat ditemukan dalam dokumentasi.

Peringatan Kasus


Tetapi apakah Anda menemukan sesuatu yang menarik? Itu sukses, dan sekarang kita akan melihat berbagai kesalahan. Dalam hal ini, saya ingin segera mencatat dua poin:

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

Peringatan PVS-Studio: V501 [CWE-571] Ada sub-ekspresi yang identik di sebelah kiri dan di sebelah kanan operator '&&': link.tx.cb && link.tx.cb pb_adv.c 377

Satu diperiksa dua kali link.tx.cb variabel yang sama . Rupanya, ini adalah salah ketik, dan variabel kedua yang akan diperiksa adalah link.tx.cb_data .

Fragmen N2, meluap buffer

Pertimbangkan fungsi net_hostname_get , yang akan digunakan nanti.

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

Dalam kasus saya, saat preprocessing, opsi yang terkait dengan cabang #else dipilih . Artinya, dalam file yang telah diproses, fungsi diimplementasikan seperti ini:
static inline const char *net_hostname_get(void)
{
  return "zephyr";
}

Fungsi mengembalikan pointer ke array 7 byte (kami memperhitungkan terminal nol di akhir baris).

Sekarang perhatikan kode yang mengarah ke keluar dari batas array.

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

Peringatan PVS-Studio: V512 [CWE-119] Panggilan fungsi 'memcpy' akan menyebabkan buffer 'net_hostname_get ()' menjadi di luar jangkauan. log_backend_net.c 114

Setelah preprocessing, MAX_HOSTNAME_LEN diperluas sebagai berikut:

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

Karenanya, saat menyalin data, string literal di luar batas terjadi. Sulit untuk memprediksi bagaimana ini akan mempengaruhi pelaksanaan program, karena ini mengarah pada perilaku yang tidak terdefinisi.

Fragmen N3, buffer overrun potensial

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 Warning: V512 [CWE-119] Panggilan fungsi 'snprintf' akan menyebabkan meluapnya buffer 'full_name'. lwm2m_rw_json.c 826

Jika kita mengganti nilai makro, gambar dari apa yang terjadi terlihat seperti ini:

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

Hanya 20 byte yang dialokasikan di bawah buffer full_name di mana string terbentuk. Dalam hal ini, bagian-bagian dari mana string terbentuk disimpan dalam buffer berukuran 20 dan 64 byte. Selain itu, konstanta 64, diteruskan ke fungsi snprintf dan dirancang untuk mencegah array pergi ke luar negeri, jelas terlalu besar!

Kode ini tidak selalu mengarah ke buffer overflow. Mungkin selalu beruntung, dan substring selalu sangat kecil. Namun, secara umum, kode ini tidak dilindungi dari meluap dengan cara apa pun dan mengandung cacat keamanan klasik CWE-119 .

Fragmen N4, ekspresi selalu benar

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

Peringatan PVS-Studio: V547 [CWE-570] Ekspresi 'len <0' selalu salah. Nilai tipe yang tidak ditandatangani tidak pernah <0. Keys.c 312

Variabel len memiliki tipe yang tidak ditandatangani dan, oleh karena itu, tidak boleh kurang dari 0. Dengan demikian, status kesalahan tidak diproses dengan cara apa pun. Di tempat lain , tipe int atau ssize_t digunakan untuk menyimpan hasil fungsi read_cb . Contoh:


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

Catatan. Semuanya tampak buruk dengan fungsi read_cb . Faktanya adalah dinyatakan seperti ini:

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

Jenis u8_t adalah karakter yang tidak ditandai.

Fungsi selalu mengembalikan hanya angka positif dari tipe unsigned char . Jika Anda meletakkan nilai ini dalam variabel bertanda tipe int atau ssize_t , semuanya sama, nilainya akan selalu positif. Karenanya, di tempat lain, memeriksa status kesalahan juga tidak berfungsi. Tapi saya tidak mempelajari studi masalah ini.

Fragmen N5, sesuatu yang sangat aneh

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

Peringatan PVS-Studio: V575 [CWE-628] Fungsi 'memcpy' tidak menyalin seluruh string. Gunakan fungsi 'strcpy / strcpy_s' untuk mempertahankan terminal null. shell.c 427

Kode aneh

Seseorang mencoba membuat analog dari fungsi strdup , tetapi ia tidak berhasil.

Mari kita mulai dengan peringatan penganalisa. Ini melaporkan bahwa fungsi memcpy menyalin baris, tetapi tidak menyalin terminal nol, dan ini sangat mencurigakan.

Tampaknya terminal 0 ini disalin di sini:

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

Tapi tidak! Ini salah ketik, karena terminal nol disalin ke dirinya sendiri! Perhatikan bahwa menulis ke array mntpt , bukan cpy_mntpt . Akibatnya, fungsi mntpt_prepare mengembalikan string yang tidak lengkap dengan nol terminal.

Bahkan, programmer ingin menulis seperti ini:

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

Namun, masih belum jelas mengapa begitu rumit! Kode ini dapat disederhanakan ke opsi berikut:

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

Fragmen N6, mendereferensi pointer sebelum validasi

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

Peringatan PVS-Studio: V595 [CWE-476] Pointer 'pub' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 708, 719. access.c 708 Pola kesalahan yang

sangat umum . Pertama, pointer direferensikan untuk menginisialisasi anggota struktur:

.send_rel = pub->send_rel,

Dan hanya kemudian mengikuti cek bahwa pointer ini bisa menjadi nol.

Fragmen N7-N9, pointer dereferencing sebelum validasi

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

Peringatan PVS-Studio: V595 [CWE-476] Pointer 'conn' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 1071, 1073. tcp2.c 1071

Sama seperti pada kasus sebelumnya. Penjelasan tidak diperlukan di sini.

Dua kesalahan serupa dapat dilihat di sini:

  • V595 [CWE-476] Pointer 'context-> tcp' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 1512, 1518. tcp.c 1512
  • V595 [CWE-476] Pointer 'fsm' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 365, 382. fsm.c 365

Fragmen N10, periksa salah

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

Peringatan PVS-Studio: V547 [CWE-570] Ekspresi '(akhir - * p) <1' ​​selalu salah. x509_crt.c 635

Lihatlah dengan seksama kondisi:

  • * p <end
  • (akhir - * p) <1

Mereka saling bertentangan.

Jika (* p <end), maka (end - * p) akan selalu memberikan nilai 1 atau lebih. Secara umum, ada sesuatu yang salah di sini, tapi saya tidak tahu cara mengejanya dengan benar.

Fragmen N11, kode tidak terjangkau

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

Peringatan PVS-Studio: V547 [CWE-571] Ekspresi 'disp' selalu benar. lv_disp.c 148

Fungsi berakhir jika disp adalah null pointer. Kemudian, sebaliknya, diperiksa bahwa pointer disp bukan null (dan ini selalu terjadi), dan fungsinya kembali menyelesaikan pekerjaannya.

Akibatnya, bagian dari kode dalam suatu fungsi tidak akan pernah mendapatkan kontrol sama sekali.

Fragmen N12, nilai balik yang aneh

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 Warning: V1001 Variabel 'len' diberikan tetapi tidak digunakan pada akhir fungsi. lwm2m_rw_oma_tlv.c 338

Fungsi ini berisi dua pernyataan pengembalian yang keduanya mengembalikan 0. Aneh bahwa fungsi selalu mengembalikan 0. Juga aneh bahwa variabel len tidak lagi digunakan setelah penugasan. Saya memiliki kecurigaan besar bahwa itu seharusnya ditulis seperti ini:

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

Fragmen N13-N16, kesalahan sinkronisasi

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

Peringatan PVS-Studio: V1020 Fungsi keluar tanpa memanggil fungsi 'k_mutex_unlock'. Periksa baris: 620, 549. nvs.c 620

Ada situasi ketika fungsi menyelesaikan tugasnya tanpa membuka kunci mutex. Seperti yang saya pahami, itu akan benar untuk menulis seperti ini:

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

Tiga kesalahan lainnya:

  • V1020 Fungsi keluar tanpa memanggil fungsi 'k_mutex_unlock'. Periksa baris: 574, 549. nvs.c 574
  • V1020 Fungsi keluar tanpa memanggil fungsi 'k_mutex_unlock'. Periksa baris: 908, 890. net_context.c 908
  • V1020 Fungsi keluar tanpa memanggil fungsi 'k_mutex_unlock'. Periksa baris: 1194, 1189. shell.c 1194

Kesimpulan


Saya harap Anda menikmatinya. Kunjungi blog kami untuk membaca tentang pemeriksaan pada proyek lain dan publikasi menarik lainnya.

Gunakan analisis statis dalam pekerjaan Anda untuk mengurangi jumlah kesalahan dan kerentanan potensial bahkan pada tahap penulisan kode. Terutama deteksi kesalahan dini adalah relevan untuk sistem embedded, memperbarui program yang seringkali memakan waktu dan proses yang mahal.

Saya juga menyarankan untuk tidak menunda dan mencoba memeriksa proyek Anda menggunakan penganalisa PVS-Studio. Lihat artikel: Bagaimana cara cepat melihat peringatan menarik yang dihasilkan oleh alat analisa PVS-Studio untuk kode C dan C ++?



Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Andrey Karpov. Memeriksa Kode Sistem Operasi Zephyr .

All Articles