Přeložit do češtiny pomocí Google Translate ...

About

The purpose of this set of mini exercises is to demonstrate why it makes sense to insert extra fields into your structure that serves the only purpose to check that the structure was not corrupted.

The provided MCC (memory-corruption-checker) structure is slightly more advanced than just adding uint32_t magic field as it can capture buffer overruns and similar issues too.

The source code is available in the examples repository.

You can easily compile and run the examples with the following commands in the mcc subdirectory.

make
make run

Basic Usage

Look into ok.c file that uses example_t structure.

Note that we call mcc_init once the structure is initialized.

Each operation on the structure is preceded by mcc_assert that checks that the structure is not corrupted and each modification must be followed by mcc_update.

This obviously clutters the code but such checks would be typically hidden in functions encapsulating access to the members so the end-user would not need to care about them that much.

And when your program starts corrupting memory, having few extra lines would be the least of your problems anyway ;-).

How it Works

You probably noticed that make run launches each program twice: in release and development mode. Our MCC implementation allows you to decide (at compile time) whether to turn on the checks or disable them. When they are disabled, all calls to MCC-related functions are effectively no-ops and even the structure is not modified (i.e. no magic fields are added at all).

How many bytes were added by MCC to the structure?

Hint.Solution.

The MCC Structures

Let’s have a look how the MCC structures are used. Look into example.h to see how example_t structure is flanked by mcc_header_t and mcc_footer_t. These structures contains checksums of the bytes between them and thus are able to detect that the structure was changed outside expected places. The structures also contains the so-called redzone - a rather big byte array that serves single purpose - catch buffer overflows without corrupting surrounding memory.

When the program is compiled in release mode, the mcc_header_t and mcc_footer_t are actually empty structures, thus there is no overhead at all.

The API

The MCC API is rather simple. You extend your structure with mcc_header_t mcc_header and mcc_footer_t mcc_footer and when new object is created, you call mcc_init. mcc_init is actually a macro that expects that your members are named mcc_header and mcc_footer.

If you need different names, you need to pass explicitly pointers to respective mcc_header_t and mcc_footer_t fields in the *_hf functions. But we recommend to stay with the defaults to make your code more readable.

Whenever you modify the object, you are supposed to call mcc_update to ensure the checksum is recomputed from actual data. This also checks that the redzone is intact.

There is also mcc_assert that checks that the checksum corresponds with the actual values and it also checks the redzone.

Notice that both mcc_update and mcc_assert are macros. Why is it better to sacrifice type safety here?

Solution.

Memory Corruption Detection Examples

The provided examples demonstrates following issues that can be detected by MCC.

Missed update means we modified the object without modifying the checksums. While this could be omission in the source code (this is one rather big disadvantage as we need to update the checksums quite often) it can also denote that we passed an invalid pointer somewhere and modified the structure inadvertently.

Overruns demonstrates situation where our structure contains a buffer (e.g. a thread name) and our code does not check its size properly. Note that the first example demonstrates a typical situation where we overflow into footer. The second example demonstrates what happens when we not only overflow “our” structure but smash the following one too.

Using MCC in Your Kernels

We do not enforce usage of MCC in your kernel but consider using some kind of memory corruption checks to ensure your structures are valid. That is, use at least a single magic number (uint32_t magic) that is different for each kernel object (heap header, threads, …) to check that the object is not corrupted.