Virtual inheritance example in C, exploiting undefined behavior? - c

In a series of articles, Dan Saks introduces a possible implementation of virtual functions in C. Relying more on static type-checking, this is a different approach as opposed to the solution of A.-T. Schreiner with void * pointers and dynamic type-checking.
Here is a stripped-down example without the vptrs and vtables of Saks' version (for the sake of simplicity, function pointers are just members of struct Base and struct Derived).
#include <stdlib.h>
#include <stdio.h>
typedef struct Base Base;
// Base "class"
struct Base {
int (*get_param)(Base const *self);
};
inline int Base_get_param(Base const *self)
{
return self->get_param(self);
}
typedef struct Derived Derived;
// Derived "class"
struct Derived {
int (*get_param)(Derived const *self);
int param;
};
Derived * Derived_new(int param)
{
Derived *self = malloc(sizeof(Derived));
if (!self) abort();
self->get_param = Derived_get_param;
self->param = param;
return self;
}
void Derived_delete(Derived *self)
{
free(self);
}
inline int Derived_get_param(Derived const *self)
{
return self->param;
}
int main()
{
Derived *d = Derived_new(5);
printf("%d\n", Derived_get_param(d));
printf("%d\n", Base_get_param((Base *) d)); // <== undefined behavior?
Derived_delete(d);
return 0;
}
The gist is the function call (and cast) Base_get_param((Base *) d). Does this mean that the function pointer int (*get_param)(Derived const *self) gets "implicitly cast" to int (*get_param)(Base const *self)? Am I exploiting undefined behavior here (according to the C99 and C11 standards) because of incompatible types?
I get the proper output both with GCC 4.8 and clang 3.4. Is there a situation where the above implementation might be broken?
There is a detailed answer here about function pointer casts and compatible types but I am not sure about this case.

This program does indeed invoke undefined behavior, you have a violation of strict aliasing rules here:
printf("%d\n", Base_get_param((Base *) d));
^^^^^^^^^
Strict aliasing rules make it undefined behavior to access an object through a pointer of a different type, although there is an exception for char* which we are allowed to use to alias without invoking undefined behavior.
Basically the compiler can optimize around the assumption that pointers of different types do not point to the same memory. Once you you invoke undefined behavior the result of your program becomes unpredictable.
Practically in other cases such as in this question I could not get the compiler to do the wrong thing but in other more complicated cases things may go wrong. See gcc, strict-aliasing, and horror stories for cases where it did cause issues. The article Type Punning, Strict Aliasing, and Optimization provides the following code:
#include <stdio.h>
void check (int *h, long *k)
{
*h = 5;
*k = 6;
printf("%d\n", *h);
}
int main (void)
{
long k;
check((int *)&k, &k);
return 0;
}
which violates strict aliasing and produces different outputs using -O1 Vs -O2.
Strict aliasing for gcc can be turned off using -fno-strict-aliasing and perhaps the author is making such an assumption although I could not find that in the article. This does disable some optimizations so it is not a costless flag.

Related

C compiler checking of a typedef'ed void *

We have an anonymous type, typedefed to void *, which is the handle for an API (all code in C11). It is deliberately void * as what it is pointing to changes depending on the platform we are compiled for and we also don't want the application to try dereferencing it. Internally we know what it should be pointing to and we cast it appropriately. This is fine, the code is public, we've been using it for years, it cannot be changed.
The problem is that we now need to introduce another one of these, and we don't want the user to get the two confused, we want the compiler to throw an error if the wrong handle is passed to one of our functions. However, all of the versions of all of the C compilers I have tried so far (GCC, Clang, MSVC) don't care; they know that the underlying type is void * and so anything goes (this is with -Wall and -Werror). Putting it another way, our typedef has not achieved anything, we might as well have just used void *. I have also tried Lint and CodeChecker, who also don't seem to care (though you could probably question my configurations for these). Note that I am not able to use -Wpedantic as we include third party code where that wouldn't fly.
I have tried making the new thing a specific typedefed pointer rather than a void * but that doesn't entirely fix things as the compiler is still happy for the caller to pass that new specific typedefed pointer into the existing functions that are expecting the existing handle typedef.
Is there (a) a way to construct a new anonymous handle such that the compiler will not allow it to be passed to the existing functions or (b) a checker that we can apply to pick the problem up, at least in our own use of these APIs?
Here is some code to illustrate the problem:
#include <stdlib.h>
typedef struct {
int contents;
} existingThing_t;
typedef void *anonExistingHandle_t;
typedef struct {
char contents[10];
} newThing_t;
typedef void *anonNewHandle_t;
typedef newThing_t *newHandle_t;
static void functionExisting(anonExistingHandle_t handle)
{
existingThing_t *pThing = (existingThing_t *) handle;
// Perform the function
(void) pThing;
}
static void functionNew(anonNewHandle_t handle)
{
newThing_t *pThing = (newThing_t *) handle;
// Perform a new function
(void) pThing;
}
int main() {
anonExistingHandle_t existingHandle = NULL;
anonNewHandle_t newHandleA = NULL;
newHandle_t newHandleB = NULL;
functionExisting(existingHandle);
functionNew(newHandleA);
// These should result in a compilation error
functionExisting(newHandleA);
functionNew(existingHandle);
functionExisting(newHandleB);
return 0;
}
Is there (a) a way to construct a new anonymous handle such that the compiler will not allow it to be passed to the existing functions
Yes, use a type that can't be implicitly converted to void *. Use a structure.
typedef struct {
struct newThing_s *p;
} anonNewHandle_t;
Anyway, your design is just flawed and disables all static compiler checks. Do not use void *, instead use structures or structures with void * inside, to enable compile checks. Research how the very, very standard FILE * works. FILE is not void.
Do not use typedef pointers. They are very confusing. https://wiki.sei.cmu.edu/confluence/display/c/DCL05-C.+Use+typedefs+of+non-pointer+types+only
I suggest rewriting your library so that you do not use void * and do not use typedef pointers.
The design, may look like the following:
// handle.h
struct handle_s;
typedef struct {
struct handle_s *p;
} handle_t;
handle_t handle_init(void);
void handle_deinit(handle_t t);
void handle_do_something(handle_t t);
// handle.c
struct handle_s {
int the_stuff_you_need;
};
handle_t handle_init(void) {
return (handle_t){
.p = calloc(1, sizeof(struct handle_s))
};
}
void handle_do_something(handle_t h) {
struct hadnle_s *t = h->p;
// etc.
}
// anotherhandle.h
// similar to above
typedef struct {
struct anotherhandle_s *p;
} anotherhandle_t;
void anotherhandle_do_something(anotherhandle_t h);
// main
int main() {
handle_t h = handle_new();
handle_do_something(h);
handle_free(h);
anotherhandle_do_something(h); // compiler error
}

Inline function, pointer to variable

I am trying to understand how the inline keyword works with pointers to variables.
Consider the following example:
struct S
{
float a;
float b;
};
inline void foo (struct S *s)
{
s->a = 5;
}
void main()
{
struct S ss;
foo(&ss);
}
When the compiler inlines the function foo, will it generate
void main()
{
struct S ss;
(&ss)->a = 5;
}
Or will it generate
void main()
{
struct S ss;
ss.a = 5;
}
In other words, will the code need to de-reference the pointer to the structure or will it understand that it needs to replace by just the structure?
In an embedded application, this would make a difference in runtime that could be significant.
The compiler is required only to generate code that achieves the result required by the semantics of the language. How a specific compiler achieves that is entirely implementation dependent. It is even possible that the code will not be in-lined at all.
To determine how your particular compiler will translate this code, you can either instruct it to output an assembly listing of the generated code or inspect the code disassembly in a debugger. The code generated may also be very different depending upon compiler options such as optimisation level.

Strict aliasing in practice

C11 code like the following is undefined behavior:
// test.c
#include <stdio.h>
struct point2d {
int x, y;
};
struct point3d {
int x, y, z;
};
typedef struct point2d point2d;
typedef struct point3d point3d;
int foo(point2d *p, point3d *q) {
p->x = -1;
p->y = -2;
q->x = 1;
q->y = 2;
q->z = 3;
return p->x;
}
int main(void) {
point3d r;
int n = foo((point2d *) &r, &r);
printf("%d\n", n);
return 0;
}
And indeed, it is:
wrc#raspberrypi:~ $ gcc -O0 test.c -o test; ./test
1
wrc#raspberrypi:~ $ gcc -O3 test.c -o test; ./test
-1
The C11 standard says (6.5/7):
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types:
a type compatible with the effective type of the object,
a qualified version of a type compatible with the effective type of the object,
a type that is the signed or unsigned type corresponding to the effective type of the object,
a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
a character type.
My question is about this kind of technique, where you try to hide information by casting a pointer to a larger struct into a pointer to a smaller struct, that does not contain all members of the larger one.
Here a summary of the relevant struct definitions:
struct _GRealArray
{
guint8 *data;
guint len;
guint alloc;
guint elt_size;
guint zero_terminated : 1;
guint clear : 1;
gint ref_count;
GDestroyNotify clear_func;
};
struct _GArray
{
gchar *data;
guint len;
};
typedef struct _GRealArray GRealArray;
typedef struct _GArray GArray;
Would such a technique already be a violation of the standard? If no: What is the difference? If yes: why doesn't it matter here? Are there some practical guidelines that allow you to de-facto violate the standard in this case with no bad consequences (contrary to the test.c example above)?
The Garray example would be subject to the same rules, if it would stress them, but it seems to be carefully avoiding that.
For user code there can never be aliasing violation between the two types, because, if I see this correctly, the GRealArray type is not visible, so all these aliasing problems can't occur in user code with these two types.
For the internal code of the implementation, there seems no point where aliasing can occur for the simple reason that there always is only one array visible in each function. or to say it more directly,
the aliasing rules only apply if there is potential aliasing.
And even if it would, if that implementation consistently uses the same type for two different pointers that it handles, the compiler still would have to assume that two such pointers can point to the same object.
BTW, this only answers your direct question not if the behavior of the code that your are pointing to is well defined. This would ask for a deep review of that code.

Using pointer functions to emulate member functions in C structs

So just for the sake if having ''fun'' I decided to emulate C++ member functions in C using pointer functions. Here is a simple code:
obj.h:
#ifndef OBJ_H
#define OBJ_H
#include <stdlib.h>
#include <stdio.h>
struct Obj{
struct pObjVar* pVar;
void (*read)(struct Obj*);
void (*set) (struct Obj*, int);
};
struct Obj* newObj();
void deleteObj(struct Obj** obj);
#endif
obj.c:
#include "obj.h"
void readValue(struct Obj* this_);
void setValue (struct Obj* this_, int mValue_);
struct pObjVar{
int mValue;
};
struct Obj* newObj(){
struct Obj* tmp = (struct Obj*) malloc(sizeof(struct Obj));
tmp->pVar = (struct pObjVar*) malloc(sizeof(struct pObjVar));
tmp->pVar->mValue = 0;
tmp->read = readValue;
tmp->set = setValue;
return tmp;
}
void deleteObj(struct Obj **obj){
free((*obj)->pVar); (*obj)->pVar = NULL;
free((*obj)); *obj = NULL;
}
void readValue(struct Obj *this_){
printf("Value = %d\n",this_->pVar->mValue);
}
void setValue(struct Obj *this_, int mValue_){
this_->pVar->mValue = mValue_;
}
main.c:
#include "obj.h"
int main(void)
{
struct Obj* a = newObj();
a->set(a, 10);
a->read(a);
deleteObj(&a);
return 0;
}
Output:
>./a.out
Value = 10
In doing this, however, I figured I had to emulate the role of implicit this pointer by explicitly passing it to my member functions. This works fine, I guess, except that it makes the whole thing look weird!
If I wanted to pass the object, why would implement the functions as member functions? The only answer I found to it was maybe in cases where you would want to have a unified interface but various implementations? (something similar to C++ virtual functions?)
What are (if any) some other reasons to emulate member functions? Also, is there any way to get around passing the explicit this_ pointer at all?
EDIT: There was problem in the original code when passing the object. I was using &a by mistake for the read/set functions. You would only need it for the deleteObj if you want to set the pointer to NULL internally.
Just another way of writing:
#define member(FUNC, ...) FUNC(this_, ## __VA_ARGS__)
int reader(struct Obj *_this) {
member(read, a, b, c);
member(getch);
return 0;
}
This can be used for implementing interfaces, inheritance and many C++ features, which were implemented like this in C with Classes times. In Linux kernel, file operations are implemented like this. File structure stores pointers to functions, so that each file system can store it's own system call handlers that operate on/with the data in the structure.
No, there is no way to do this automatically in C. The standard preprocessor is not competent enough to do the transformations.
There is also now way for a function to find out that it was called like a->func(10). Inside the function it is just func(10).
When Bjarne Stroustrup started designing C++, he wrote a special preprocessor/compiler Cfront for this.
In reality, C++ doesn't really store pointers to (non-virtual) functions. It just transforms a->set(10) to something like struct_Obj_set(a, 10) while compiling the code.

What defines an opaque type in C, and when are they necessary and/or useful?

I've seen the concept of 'opaque types' thrown around a bit but I really haven't found a succinct answer as to what defines an opaque type in C and more importantly what problems they allow us to solve with their existence. Thanks
It is the most generally used for library purpose. The main principe behind Opaque type in c is to use data though its pointer in order to hide data handling implementation. Since the implementation is hidden, you can modify the library without recompiling any program which depend on it (if the interface is respected)
eg:
version 1:
// header file
struct s;
int s_init(struct s **x);
int s_f(struct s *x);
int s_g(struct s *x);
// source file
struct s { int x; }
int s_init(struct s **x) { *x = malloc(...); }
int s_f(..) { ... }
int s_g(..) { ... }
version 2
// header file
struct s;
int s_init(struct s **x);
int s_f(struct s *x);
int s_g(struct s *x);
// source file
struct s { int y; int x; }
int s_init(struct s **x) { *x = malloc(...); }
int s_f(..) { ... }
int s_g(..) { ... }
From your program side, nothing changed! and as said previously, no need to recompile every single program which rely on it.
In my understanding, opaque types are those which allow you to hold a handle (i.e., a pointer) to an structure, but not modify or view its contents directly (if you are allowed to at all, you do so through helper functions which understand the internal structure).
Opaque types are, in part, a way to make C more object-oriented. They allow encapsulation, so that the internal details of a type can change--or be implemented differently in different platforms/situations--without the code that uses it having to change.
An opaque type is a type which is exposed in APIs via a pointer but never concretely defined.

Resources