C
Backstory
My wife inherited a cat from family.† Unfortunately, I am very allergic to animals. The cat was well past its prime and should have been euthanized even before we got it, but she could not bring herself to get rid of it due to its sentimental value. I hatched a plan to end my its suffering.
We were going on an extended vacation, but she did not want to board the cat at the veterinarian's office. She was concerned about it contracting illness or being mistreated. I created an automatic cat feeder so that we could leave it at home. I wrote the microcontroller's firmware in C. The file containing main
looked similar to the code below.
However, my wife is also a programmer and knew my feelings towards the cat, so she insisted on a code-review before agreeing to leave it at home unattended. She had several concerns, including:
main
does not have a standards compliant signature (for a hosted implementation)
main
does not return a value
tempTm
is used uninitialized since malloc
was called instead of calloc
- the return value of
malloc
should not be cast
- the microcontroller time may be inaccurate or roll over (similar to the Y2K or Unix time 2038 problems)
- the
elapsedTime
variable may not have sufficient range
It took a lot of convincing, but she finally agreed that theses weren't problems for various reasons (it didn't hurt that we were already late for our flight). Since there was no time for live testing, she approved the code and we went on vacation. When we returned a few weeks later, my the cat's misery was over (though as a result I've now got plenty more).
† Entirely fictitious scenario, no worries.
Code
#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;
}
}
}
Undefined behavior:
For those who don't want to bother finding the UB themselves:
There's definitely local-specific, unspecified, and implementation-defined behavior in this code, but that all should work correctly. The problem is in the following lines of code:
struct tm * tempTm //...
//...
memcpy(&tempTm, localtime(&nowTime), sizeof(struct tm));
memcpy
overwrites the tempTM
pointer instead of the object it points to, smashing the stack. This overwrites, in addition to other things, elapsedTime
and loopIterationsSinceFeed
. Here's an example run where I printed out the values:
pre-smash : elapsedTime=1394210441 loopIterationsSinceFeed=1
post-smash : elapsedTime=65 loopIterationsSinceFeed=0
Probability of killing the cat:
- Given the constrained execution environment and build chain, the undefined behavior always occurs.
- Similarly, the undefined behavior always prevents the cat feeder from working as intended (or rather, allows it to "work" as intended).
- If the feeder does not work, it is extremely likely the cat will die. This is not a cat that can fend for itself, and I failed to ask the neighbor to look in on it.
I estimate that the cat dies with probability 0.995.
I don't get this. For this purpose, all programs with UB are fungible. – R. Martinho Fernandes – 2016-05-09T11:08:52.337
2I don't really understand the calculation of the scoring. For example, why does number 3 give
3%
as probability, and number 4 give93%
? – ProgramFOX – 2014-03-04T17:56:25.8533@ProgramFOX, The probabilities are my estimates, that's all. Note that the probability is no more than 1, so it's no more than a tie breaker. – ugoren – 2014-03-04T17:58:01.547
1@n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳, Just try not to violate rule 4. – ugoren – 2014-03-04T17:59:07.970
You might want to add in a stipulation that the total probability must be greater than 0. Are there any special rules for those of us who don't own a cat? – Josh – 2014-03-04T18:01:44.777
50>
2@Geobits, but the cat's death isn't "a result of the aforementioned UB". – Oberon – 2014-03-04T18:24:31.583
5@Oberon I'd kill myself if I couldn't come up with a single way to create an UB after programming for so long. Then the cat would no longer be in my possession. Invoking the UB keeps me alive, so that "... ending its life while in your ownership , as a result...". Ambiguous parsing FTW. – Geobits – 2014-03-04T18:28:22.567
1Are special hardware interfaces allowed, eg. solenoids, shotguns? – Martin James – 2014-03-04T23:24:54.973
@ProgramFOX the chance that you will shoot your cat based on said message are very low (3%). The chance of it dying when shot is high (93%). – Roberto – 2014-03-04T23:29:13.613
@MartinJames, Everything is allowed. Will it get you upvotes? I don't know. – ugoren – 2014-03-04T23:41:03.167
8I'd be more amused if someone reinterprets this to kill the
cat
command or something of that sort. – keshlam – 2014-03-05T00:24:32.5175-1 I love cats. Why cats? Why not killinig vermin? – V-X – 2014-03-05T09:37:07.650
2
@V-x Given the uncertainty, i'm pretty sure it's related to Schrödinger's cat.
– Cees Timmerman – 2014-03-05T13:14:10.3101That's obvious. I wanted to compensate a bit that I'm not happy about the manifestation of cat hate. – V-X – 2014-03-05T21:46:53.200
3@V-X, Where's the cat hate? I want to understand how UB can kill cats, so we could fight this menacing problem and save feline lives. – ugoren – 2014-03-06T02:16:31.780
@V-X The rules say that no animals may actually be harmed. This is a clue for you that this is not to be taken seriously. If Schrödinger had made his thought experiment using a plague-infested rat, this question would have been about a plague-infested rat. The fact that *NIX has a command-line tool
cat
is just a serendipitous coincidence that improves the joke. – steveha – 2014-03-06T03:30:21.687Calling
printf
without a prototype is undefined behavior, period. – Kaz – 2014-03-06T08:05:16.8631"No animals may be harmed in the production of your answer." Can I at least harm a cat while testing after production?.... Gotta make sure to get those percentages right :D – Kevin – 2014-03-06T12:00:32.430
5@Kevin, Sorry, but cat lovers here would downvote me to death if I let you do that. Try testing it with tofu or something. – ugoren – 2014-03-06T15:27:21.773
When I saw the question title in the hot network questions, I first thought this had been asked at [pets.SE] :-) – Bergi – 2014-03-07T03:25:54.077
22If a cat is to be eaten, Python is a solution. – Nicolas Barbulesco – 2014-03-07T09:09:35.573
1Felis catus is your taxonomic nomenclature, An endothermic quadruped, carnivorous by nature; Your visual, olfactory, and auditory senses Contribute to your hunting skills and natural defenses-> Sorry had to do it :-) – Steven Wood – 2014-03-07T09:49:07.297
4Simply letting a cat live wildly is undefined behavior. Chance of death is 100%. Do I win all your triangles? :D – Vercas – 2014-03-08T17:42:22.483
Why is everybody struggling for a mechanism by which the felis catus dies? That's the simple part: an omnipotent being smites it. I'll leave it to everybody's imagination why that happens and how a program error my cause that situation to arise... ;-) – R.. GitHub STOP HELPING ICE – 2014-03-09T06:29:34.750
Hmm. Maybe I'll build an automatic cat feeder with a Raspberry Pi, and implement it with UB that can cause it to malfunction. – Cruncher – 2014-05-20T18:25:37.000