1
mirror of https://github.com/flipperdevices/flipperzero-firmware.git synced 2025-12-12 04:41:26 +04:00

[FL-3759] Fix expansion protocol crash when fed lots of garbage (#3409)

* Fix crash caused by garbage input
* Add unit tests for garbage input
* Enable applications to disable and then restore expansion module support
* GPIO App: disable expansion on app start and re-enable on exit

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
Georgii Surkov
2024-01-30 16:54:25 +03:00
committed by GitHub
parent c8ea167a06
commit e6f078eeb7
12 changed files with 166 additions and 118 deletions

View File

@@ -394,30 +394,18 @@ void expansion_on_system_start(void* arg) {
Expansion* instance = expansion_alloc();
furi_record_create(RECORD_EXPANSION, instance);
ExpansionSettings settings = {};
if(!expansion_settings_load(&settings)) {
expansion_settings_save(&settings);
} else if(settings.uart_index < FuriHalSerialIdMax) {
expansion_enable(instance, settings.uart_index);
}
expansion_enable(instance);
}
// Public API functions
void expansion_enable(Expansion* instance, FuriHalSerialId serial_id) {
expansion_disable(instance);
furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk);
instance->serial_id = serial_id;
instance->state = ExpansionStateEnabled;
furi_hal_serial_control_set_expansion_callback(
instance->serial_id, expansion_detect_callback, instance);
furi_mutex_release(instance->state_mutex);
FURI_LOG_D(TAG, "Detection enabled");
void expansion_enable(Expansion* instance) {
ExpansionSettings settings = {};
if(!expansion_settings_load(&settings)) {
expansion_settings_save(&settings);
} else if(settings.uart_index < FuriHalSerialIdMax) {
expansion_set_listen_serial(instance, settings.uart_index);
}
}
void expansion_disable(Expansion* instance) {
@@ -435,3 +423,19 @@ void expansion_disable(Expansion* instance) {
furi_mutex_release(instance->state_mutex);
}
void expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id) {
expansion_disable(instance);
furi_check(furi_mutex_acquire(instance->state_mutex, FuriWaitForever) == FuriStatusOk);
instance->serial_id = serial_id;
instance->state = ExpansionStateEnabled;
furi_hal_serial_control_set_expansion_callback(
instance->serial_id, expansion_detect_callback, instance);
furi_mutex_release(instance->state_mutex);
FURI_LOG_D(TAG, "Detection enabled");
}

View File

@@ -21,18 +21,17 @@ extern "C" {
typedef struct Expansion Expansion;
/**
* @brief Enable support for expansion modules on designated serial port.
* @brief Enable support for expansion modules.
*
* Only one serial port can be used to communicate with an expansion
* module at a time.
* Calling this function will load user settings and enable
* expansion module support on the serial port specified in said settings.
*
* Calling this function when expansion module support is already enabled
* will first disable the previous setting, then enable the current one.
* If expansion module support was disabled in settings, this function
* does nothing.
*
* @param[in,out] instance pointer to the Expansion instance.
* @param[in] serial_id numerical identifier of the serial.
*/
void expansion_enable(Expansion* instance, FuriHalSerialId serial_id);
void expansion_enable(Expansion* instance);
/**
* @brief Disable support for expansion modules.
@@ -41,10 +40,34 @@ void expansion_enable(Expansion* instance, FuriHalSerialId serial_id);
* expansion module (if any), release the serial handle and
* reset the respective pins to the default state.
*
* @note Applications requiring serial port access MUST call
* this function BEFORE calling furi_hal_serial_control_acquire().
* Similarly, an expansion_enable() call MUST be made right AFTER
* a call to furi_hal_serial_control_release() to ensure that
* the user settings are properly restored.
*
* @param[in,out] instance pointer to the Expansion instance.
*/
void expansion_disable(Expansion* instance);
/**
* @brief Enable support for expansion modules on designated serial port.
*
* Only one serial port can be used to communicate with an expansion
* module at a time.
*
* Calling this function when expansion module support is already enabled
* will first disable the previous setting, then enable the current one.
*
* @warning This function does not respect user settings for expansion modules,
* so calling it might leave the system in inconsistent state. Avoid using it
* unless absolutely necessary.
*
* @param[in,out] instance pointer to the Expansion instance.
* @param[in] serial_id numerical identifier of the serial.
*/
void expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id);
#ifdef __cplusplus
}
#endif

View File

@@ -193,11 +193,18 @@ static inline size_t expansion_frame_get_encoded_size(const ExpansionFrame* fram
*
* @param[in] frame pointer to the frame to be evaluated.
* @param[in] received_size number of bytes currently availabe for evaluation.
* @returns number of bytes needed for a complete frame.
* @param[out] remaining_size pointer to the variable to contain the number of bytes needed for a complete frame.
* @returns true if the remaining size could be calculated, false on error.
*/
static inline size_t
expansion_frame_get_remaining_size(const ExpansionFrame* frame, size_t received_size) {
if(received_size < sizeof(ExpansionFrameHeader)) return sizeof(ExpansionFrameHeader);
static inline bool expansion_frame_get_remaining_size(
const ExpansionFrame* frame,
size_t received_size,
size_t* remaining_size) {
if(received_size < sizeof(ExpansionFrameHeader)) {
// Frame type is unknown as of now
*remaining_size = sizeof(ExpansionFrameHeader);
return true;
}
const size_t received_content_size = received_size - sizeof(ExpansionFrameHeader);
size_t content_size;
@@ -217,16 +224,26 @@ static inline size_t
break;
case ExpansionFrameTypeData:
if(received_content_size < sizeof(frame->content.data.size)) {
// Data size is unknown as of now
content_size = sizeof(frame->content.data.size);
} else if(frame->content.data.size > sizeof(frame->content.data.bytes)) {
// Malformed frame or garbage input
return false;
} else {
content_size = sizeof(frame->content.data.size) + frame->content.data.size;
}
break;
default:
return SIZE_MAX;
return false;
}
return content_size > received_content_size ? content_size - received_content_size : 0;
if(content_size > received_content_size) {
*remaining_size = content_size - received_content_size;
} else {
*remaining_size = 0;
}
return true;
}
/**
@@ -275,9 +292,7 @@ static inline ExpansionProtocolStatus expansion_protocol_decode(
size_t remaining_size;
while(true) {
remaining_size = expansion_frame_get_remaining_size(frame, total_size);
if(remaining_size == SIZE_MAX) {
if(!expansion_frame_get_remaining_size(frame, total_size, &remaining_size)) {
return ExpansionProtocolStatusErrorFormat;
} else if(remaining_size == 0) {
break;