Potential Design Pattern bug in Chibi Abstract peripheral interfaces
Posted: Sat Jan 27, 2018 3:45 am
Hi All,
We are working on writing external device drivers for certain Accelerometers/Gyros and Magnetometers using the abstract peripheral interface. While going through the design pattern(object oriented C having virtual function implementations), I found a potential flaw in the design pattern:
- ChibiOS version or subversion revision number : 17.6.2
- Compiler : gcc
- Platform and board : STM32, STM32L476RG-Nucleo
- Nature of the problem : Potential pointer dereference in polymorphic type inheritance models written in C for abstract peripheral devices
- Failure mode: Accessing polymorphic base class member data variables from a derived class object, using a base class pointer.
Eg: in the driver lis302dl.h, driver for accelerometer, the derived class of LIS302DLDriver from BaseAccelerometer, which in turn is derived from BaseSensor, has 3 virtual tables and 3 virtual pointers for each of them. So, if user creates an instance of LIS302DLDriver, typecasts it's address to a pointer of type BaseAccelerometer, then tries to access data members of BaseAccelerometer using that pointer, it would get garbage data since, the offset of the _base_accelerometer_data is actually the second virtual table of LIS302DLDriver declaration. (Luckily, for accelerometer abstract peripheral interface, there is no data for the base class, so this bug is missed.)
Yusuf Husainy
We are working on writing external device drivers for certain Accelerometers/Gyros and Magnetometers using the abstract peripheral interface. While going through the design pattern(object oriented C having virtual function implementations), I found a potential flaw in the design pattern:
- ChibiOS version or subversion revision number : 17.6.2
- Compiler : gcc
- Platform and board : STM32, STM32L476RG-Nucleo
- Nature of the problem : Potential pointer dereference in polymorphic type inheritance models written in C for abstract peripheral devices
- Failure mode: Accessing polymorphic base class member data variables from a derived class object, using a base class pointer.
Eg: in the driver lis302dl.h, driver for accelerometer, the derived class of LIS302DLDriver from BaseAccelerometer, which in turn is derived from BaseSensor, has 3 virtual tables and 3 virtual pointers for each of them. So, if user creates an instance of LIS302DLDriver, typecasts it's address to a pointer of type BaseAccelerometer, then tries to access data members of BaseAccelerometer using that pointer, it would get garbage data since, the offset of the _base_accelerometer_data is actually the second virtual table of LIS302DLDriver declaration. (Luckily, for accelerometer abstract peripheral interface, there is no data for the base class, so this bug is missed.)
- The following example code snippet wouldn't work(assume BaseSensor has a data variable called 'msg_t stateSensor') (just for illustration):
In hal_sensors.h:And in main.c:Code: Select all
/**
* @brief @p BaseSensor specific data.
* @note It is empty because @p BaseSensor is only an interface
* without implementation.
*/
#define _base_sensor_data \
msg_t stateSensor;In above snippet, stateViewedFromBaseAccelerometerPointer doesn't get updated with value of '1'.Code: Select all
LIS302DLDriver derivedAccelerometerObject = {0};
LIS302DLDriver * derivedAccelerometerPointer = &derivedAccelerometerObject;
BaseSensor * baseAccelerometerPointer = (BaseSensor *)&derivedAccelerometerObject;
derivedAccelerometerPointer->stateSensor = 1;
msg_t stateViewedFromBaseAccelerometerPointer = baseAccelerometerPointer->stateSensor; - A potential solution would be is to define class heirarchy of hal_sensors and it's derived forms as:And then the macro definitions for function call helpers as:
Code: Select all
/**
* @brief Base sensor class.
* @details This class represents a generic blocking unbuffered sequential
* data stream.
*/
typedef struct {
/** @brief Virtual Methods Table.*/
const struct BaseSensorVMT *vptr;
_base_sensor_data
} BaseSensor;
/**
* @brief Base accelerometer class.
* @details This class represents a generic a generic accelerometer.
*/
typedef struct {
/** @brief Virtual Methods Table.*/
const struct BaseAccelerometerVMT *vptr;
_base_sensor_data
} BaseAccelerometer;
/**
* @extends BaseAccelerometer
*
* @brief LIS302DL 3-axis accelerometer class.
* @details This class extends @p BaseAccelerometer by adding physical
* driver implementation.
*/
struct LIS302DLDriver {
/** @brief LIS302DL Virtual Methods Table. */
const struct LIS302DLVMT *vptr;
_lis302dl_data
};Code: Select all
/**
* @name Macro Functions (BaseSensor)
* @{
*/
/**
* @brief Sensors get channels number.
*
* @param[in] ip pointer to a @p BaseSensor or derived class.
* @return The number of channels of the BaseSensor
*
* @api
*/
#define sensorGetChannelNumber(ip) (ip)->vptr->get_channels_number(ip)
/**
* @brief Sensors read raw data.
*
* @param[in] ip pointer to a @p BaseSensor or derived class.
* @param[in] dp pointer to a data array.
*
* @return The operation status.
* @retval MSG_OK if the function succeeded.
* @retval MSG_RESET if one or more errors occurred.
*
* @api
*/
#define sensorReadRaw(ip, dp) (ip)->vptr->read_raw(ip, dp)
...similarly for all derived classes of BaseSensor
Yusuf Husainy