STECE2024, Embedded Computing 1: Exam (2025-09-18)#

Below you find descriptions of “situations”, like the code of a program that you are supposed to analyse, together with an explanation.

After each such situation description you see a table of “statements”, where each row has four fields.

  • Statement: a statement about the situation. The statement can either be true or false.

  • True: check that field with an ‘x’ if you think the statement is true.

  • False: check that field with an ‘x’ if you think the statement is false.

  • Why: This field is optional. If you are unsure about your answer, write a few words explaining why you think your answer is correct. If your reasoning is not complete nonsense, /me might diverge a little from his pure boolean “correct/incorrect” grading decision.

Not applying a ‘x’ to any of the “True” and “False” fields in a single row will render your answer incorrect, obviously, as does checking both fields.

No computers, no cellphones, no AI glasses, no earpods, no internet! Only pen and paper.

Grading algorithm

Inside one situation,

  • Each row counts as 1

  • The statement’s outcome is calculated as

    • The sum of correct rows

    • Minus the sum of incorrect rows

Globally,

  • Each situation has a weight attached (situations, together with their statements, are not equal in their difficulty)

  • The sum of the weighted situation outcomes is then scaled up to 40% (the theoretical part) as per syllabus

Classic Programmer’s Mistakes, Part 1 (Operating Systems)#

The program below exhibits a property which makes it uncomfortable for a CPU to run on.

unsigned int halt = 666;

int main()
{
    while (halt != 0);
    return 0;
}

Statement

True

False

Why

On a single-processor system, a process running that program’s code will never release the processor, and no other process will ever have a chance to run again.

.

.

On a single-processor system, the OS kernel will interrupt the rogue process after a short time (a so-called time-slice), put it on a queue of “runnable” (but not running) processes, and give another process from that queue a chance to run.

.

.

On a multi-processor (say, N) system, when you run N instances of the program, the entire system will come to a halt because each processor is saturated with one such idiot.

.

.

On a multi-processor system, the OS will increase the CPU frequency to satisfy the needs of its users. This can lead to not only severe damage of the CPU, but also your house may burn down.

.

.

Classic Programmer’s Mistakes, Part 2 (Operating Systems)#

Say you compile and run the program below, which is buggy. What is the bug? How large can the resulting suffering become?

int* get_address()
{
    int value = 42;
    return &value;
}

int main()
{
    int* address = get_address();
    *address = 42;
    return 0;
}

Statement

True

False

Why

The function get_address() returns an uninitialized value.

.

.

The pointer returned by function get_address() points to something that is not valid anymore after return, thus turning every access to it into undefined behavior.

.

.

42 points right into the memory location where the Raspberry Pi’s video memory is routed to. Writing to that location might lead to funky artifacts on your screen, but otherwise no effects are expected.

.

.

The compiler may have optimized out the function call and its stack frame, giving the caller (main()) an address that is outright invalid in the process’s address space. As a result, the OS intercepts a hardware exception from the Memory Management Unit (MMU) which says, “At that position there is no memory allocated for the current process”. As a result the OS terminates the process.

.

.

The compiler may not have optimized anything. The memory of the stack frame that was allocated for the call to get_address() remains valid after the call, and the program proceeds as if nothing had happened.

.

.

Device Access Permissions (Operating Systems)#

User jfasch wants to control some peripheral using an IO pin (read and write access on /dev/gpiochip0 is required to control an IO pin). Given the following situation, answer the questions below.

$ ls -l /dev/gpiochip0
crw-rw---- 1 root gpio 254, 0 Jun 15 22:52 /dev/gpiochip0
$ id
uid=1001(jfasch) gid=1001(jfasch) groups=1001(jfasch)

Statement

True

False

Why

User jfasch can use GPIO pins right away.

.

.

User jfasch can easily become root, and then use GPIO pins.

.

.

After being added to the wheel group, user jfasch can use GPIO pins.

.

.

After being added to the gpio group, user jfasch can use GPIO pins.

.

.

Directory Access Permissions (Operating Systems)#

Given a directory with the following permissions (and the permissions on its contained files),

$ ls -dl ~/some-directory/
drwxr-xr-x 2 jfasch jfasch 4096 Jun 25 19:26 /home/jfasch/some-directory/
$ ls -l ~/some-directory/
total 4
-rw-r--r-- 1 jfasch jfasch 1623 Jun 25 19:31 my-bitcoins.money

Statement

True

False

Why

Anybody who is logged in on the system whill be able to read ~/some-directory/my-bitcoins.money

.

.

chmod 666 ~/some-directory will secure ~/some-directory/my-bitcoins.money from read access by others

.

.

chmod go-x ~/some-directory will secure ~/some-directory/my-bitcoins.money from read access by others

.

.

chmod go-r ~/some-directory/my-bitcoins.money will secure ~/some-directory/my-bitcoins.money from read access by others

.

.

chmod go-w ~/some-directory/my-bitcoins.money will secure ~/some-directory/my-bitcoins.money from read access by others

.

.

The Foo Family (Object Oriented Programming)#

Consider the following (nonsensical) class hierarchy, and the associated main program. Answer the questions below.

@startuml

interface IFoo {
  + bar()
}

class AFoo {
  + bar()
}
class BFoo {
  + bar()
}

IFoo <|.. AFoo
IFoo <|.. BFoo

@enduml

#pragma once

#include <iostream>

class IFoo
{
public:
    virtual ~IFoo() = default;
    virtual int bar() const = 0;
};

class AFoo : public IFoo
{
public:
    int bar() const override
    {
        std::cout << "AFoo::bar()" << std::endl;
        return 42;
    }
};

class BFoo : public IFoo
{
public:
    BFoo(int i) : _i(i) {}
    int bar() const override
    {
        std::cout << "BFoo::bar()" << std::endl;
        return _i;
    }

private:
    int _i;
};
#include "the-foos.h"
#include <string>

int main(int argc, char** argv)
{
    std::string the_argument = argv[1];
    IFoo* foo;

    if (the_argument == "a") {
        foo = new AFoo();
    }
    else if (the_argument == "b") {
        foo = new BFoo(666);
    }

    std::cout << foo->bar() << std::endl;

    delete foo;

    return 0;
}

Statement

True

False

Why

A call as shown gives the output as shown

$ ./program a
AFoo::bar()
42

.

.

A call as shown gives the output as shown

$ ./program b
BFoo::bar()
666

.

.

A call as shown will do nothing because foo is null

$ ./program c
...?...

.

.

A call as shown will likely crash because foo has undefined value

$ ./program c
...?...

.

.