C
Backstory
Istri saya mewarisi seekor kucing dari keluarga. † Sayangnya, saya sangat alergi terhadap hewan. Kucing itu sudah melewati masa jayanya dan seharusnya sudah di-eutanasia bahkan sebelum kita mendapatkannya, tetapi dia tidak bisa menyingkirkannya karena nilai sentimentalnya. Saya membuat rencana untuk mengakhiri penderitaan saya .
Kami akan berlibur panjang, tetapi dia tidak ingin menaiki kucing di kantor dokter hewan. Dia khawatir tentang itu tertular penyakit atau dianiaya. Saya membuat pengumpan kucing otomatis sehingga kami bisa meninggalkannya di rumah. Saya menulis firmware mikrokontroler di C. File yang berisi main
tampak mirip dengan kode di bawah ini.
Namun, istri saya juga seorang programmer dan tahu perasaan saya terhadap kucing itu, jadi dia bersikeras melakukan review kode sebelum setuju untuk meninggalkannya di rumah tanpa pengawasan. Dia memiliki beberapa masalah, termasuk:
main
tidak memiliki tanda tangan yang memenuhi standar (untuk implementasi yang di-host)
main
tidak mengembalikan nilai
tempTm
digunakan tidak diinisialisasi sejak malloc
dipanggil, bukancalloc
- nilai pengembalian
malloc
tidak boleh dilemparkan
- waktu mikrokontroler mungkin tidak akurat atau terguling (mirip dengan masalah waktu Y2K atau Unix 2038)
- yang
elapsedTime
variabel mungkin tidak memiliki rentang yang cukup
Butuh banyak meyakinkan, tetapi dia akhirnya setuju bahwa tesis ini tidak masalah karena berbagai alasan (tidak ada salahnya kita sudah terlambat untuk penerbangan kami). Karena tidak ada waktu untuk pengujian langsung, dia menyetujui kode dan kami pergi berlibur. Ketika kami kembali beberapa minggu kemudian, kesengsaraan kucing saya telah berakhir (meskipun sebagai hasilnya saya sekarang memiliki lebih banyak lagi).
Scenario Skenario sepenuhnya fiktif, tidak perlu khawatir.
Kode
#include <time.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
//#include "feedcat.h"
// contains extern void FeedCat(struct tm *);
// implemented in feedcat.c
// stub included here for demonstration only
#include <stdio.h>
// passed by pointer to avoid putting large structure on stack (which is very limited)
void FeedCat(struct tm *amPm)
{
if(amPm->tm_hour >= 12)
printf("Feeding cat dinner portion\n");
else
printf("Feeding cat breakfast portion\n");
}
// fallback value calculated based on MCU clock rate and average CPI
const uintmax_t FALLBACK_COUNTER_LIMIT = UINTMAX_MAX;
int main (void (*irqVector)(void))
{
// small stack variables
// seconds since last feed
int elapsedTime = 0;
// fallback fail-safe counter
uintmax_t loopIterationsSinceFeed = 0;
// last time cat was fed
time_t lastFeedingTime;
// current time
time_t nowTime;
// large struct on the heap
// stores converted calendar time to help determine how much food to
// dispense (morning vs. evening)
struct tm * tempTm = (struct tm *)malloc(sizeof(struct tm));
// assume the cat hasn't been fed for a long time (in case, for instance,
// the feeder lost power), so make sure it's fed the first time through
lastFeedingTime = (size_t)(-1);
while(1)
{
// increment fallback counter to protect in case of time loss
// or other anomaly
loopIterationsSinceFeed++;
// get current time, write into to nowTime
time(&nowTime);
// calculate time since last feeding
elapsedTime = (int)difftime(nowTime, lastFeedingTime);
// get calendar time, write into tempTm since localtime uses an
// internal static variable
memcpy(&tempTm, localtime(&nowTime), sizeof(struct tm));
// feed the cat if 12 hours have elapsed or if our fallback
// counter reaches the limit
if( elapsedTime >= 12*60*60 ||
loopIterationsSinceFeed >= FALLBACK_COUNTER_LIMIT)
{
// dispense food
FeedCat(tempTm);
// update last feeding time
time(&lastFeedingTime);
// reset fallback counter
loopIterationsSinceFeed = 0;
}
}
}
Perilaku tidak terdefinisi:
Bagi yang tidak mau repot-repot mencari UB sendiri:
Jelas ada perilaku spesifik lokal, tidak spesifik, dan terdefinisi implementasi dalam kode ini, tetapi semua harus bekerja dengan benar. Masalahnya adalah pada baris kode berikut:
struct tm * tempTm //...
//...
memcpy(&tempTm, localtime(&nowTime), sizeof(struct tm));
memcpy
menimpa tempTM
pointer bukan objek yang ditunjuknya, menghancurkan tumpukan. Ini menimpa, selain hal-hal lain, elapsedTime
dan loopIterationsSinceFeed
. Berikut ini contoh menjalankan tempat saya mencetak nilai:
pre-smash : elapsedTime=1394210441 loopIterationsSinceFeed=1
post-smash : elapsedTime=65 loopIterationsSinceFeed=0
Kemungkinan membunuh kucing:
- Mengingat lingkungan eksekusi yang dibatasi dan rantai build, perilaku yang tidak terdefinisi selalu terjadi.
- Demikian pula, perilaku yang tidak terdefinisi selalu mencegah pengumpan kucing bekerja sebagaimana dimaksud (atau lebih tepatnya, memungkinkannya untuk "bekerja" sebagaimana dimaksud).
- Jika pengumpan tidak berfungsi, kemungkinan besar kucing akan mati. Ini bukan kucing yang bisa menjaga dirinya sendiri, dan saya gagal meminta tetangga untuk melihatnya.
Saya memperkirakan bahwa kucing itu mati dengan probabilitas 0,995 .