Skip to content

Commit 351c63f

Browse files
committed
[Fix] Issue-567: Out of Memory crashes in Sub-Ghz
1 parent 8ed809f commit 351c63f

12 files changed

Lines changed: 125 additions & 15 deletions

File tree

applications/main/subghz/helpers/subghz_txrx.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
#include <power/power_service/power.h>
99

10+
#include <furi/core/memmgr.h>
11+
#include <furi/core/memmgr_heap.h>
12+
1013
#define TAG "SubGhzTxRx"
1114

1215
static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) {
@@ -322,6 +325,21 @@ SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat*
322325
FURI_LOG_E(TAG, "Missing Protocol");
323326
break;
324327
}
328+
329+
size_t need_heap = SUBGHZ_TX_MIN_HEAP;
330+
size_t need_block = SUBGHZ_TX_MIN_BLOCK;
331+
if(furi_string_equal(temp_str, "RAW")) {
332+
need_heap = SUBGHZ_TX_MIN_HEAP_RAW;
333+
need_block = SUBGHZ_TX_MIN_BLOCK_RAW;
334+
}
335+
instance->tx_min_heap_required = need_heap;
336+
if(memmgr_get_free_heap() < need_heap ||
337+
memmgr_heap_get_max_free_block() < need_block) {
338+
FURI_LOG_E(TAG, "Not enough memory to start TX");
339+
ret = SubGhzTxRxStartTxStateErrorMemory;
340+
break;
341+
}
342+
325343
ret = SubGhzTxRxStartTxStateOk;
326344

327345
SubGhzRadioPreset* preset = instance->preset;
@@ -416,6 +434,11 @@ FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) {
416434
return instance->fff_data;
417435
}
418436

437+
size_t subghz_txrx_get_tx_min_heap_required(SubGhzTxRx* instance) {
438+
furi_assert(instance);
439+
return instance->tx_min_heap_required;
440+
}
441+
419442
SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance) {
420443
furi_assert(instance);
421444
return instance->setting;

applications/main/subghz/helpers/subghz_txrx.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,18 @@ typedef enum {
1717
SubGhzTxRxStartTxStateOk,
1818
SubGhzTxRxStartTxStateErrorOnlyRx,
1919
SubGhzTxRxStartTxStateErrorParserOthers,
20+
SubGhzTxRxStartTxStateErrorMemory,
2021
} SubGhzTxRxStartTxState;
2122

23+
#define SUBGHZ_TX_MIN_HEAP (8u * 1024u)
24+
#define SUBGHZ_TX_MIN_BLOCK (5u * 1024u)
25+
#define SUBGHZ_TX_MIN_HEAP_RAW (14u * 1024u)
26+
// RAW streaming allocates two large contiguous buffers (stream ~4 KB and the
27+
// pre-reserved line buffer ~4.6 KB). Require the largest free block to hold both
28+
#define SUBGHZ_TX_MIN_BLOCK_RAW (9u * 1024u)
29+
30+
size_t subghz_txrx_get_tx_min_heap_required(SubGhzTxRx* instance);
31+
2232
/**
2333
* Allocate SubGhzTxRx
2434
*

applications/main/subghz/helpers/subghz_txrx_i.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,7 @@ struct SubGhzTxRx {
2727
SubGhzTxRxNeedSaveCallback need_save_callback;
2828
void* need_save_context;
2929

30+
size_t tx_min_heap_required;
31+
3032
bool debug_pin_state;
3133
};

applications/main/subghz/scenes/subghz_scene_decode_raw.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
#include "../subghz_i.h"
2+
#include <furi/core/memmgr.h>
3+
#include <furi/core/memmgr_heap.h>
24

35
#define TAG "SubGhzDecodeRaw"
46

7+
#define SUBGHZ_DECODE_RAW_MIN_HEAP (12u * 1024u)
8+
// The decode worker allocates two large contiguous buffers (stream ~4 KB and the
9+
// pre-reserved line buffer ~4.6 KB). Require the largest free block to hold both,
10+
#define SUBGHZ_DECODE_RAW_MIN_BLOCK (9u * 1024u)
11+
512
#define SAMPLES_TO_READ_PER_TICK 400
613

714
static void subghz_scene_receiver_update_statusbar(void* context) {
@@ -145,6 +152,14 @@ bool subghz_scene_decode_raw_start(SubGhz* subghz) {
145152
} while(false);
146153

147154
if(success) {
155+
if(memmgr_get_free_heap() < SUBGHZ_DECODE_RAW_MIN_HEAP ||
156+
memmgr_heap_get_max_free_block() < SUBGHZ_DECODE_RAW_MIN_BLOCK) {
157+
FURI_LOG_E(TAG, "Not enough memory to decode");
158+
subghz->decode_raw_file_worker_encoder = NULL;
159+
furi_string_free(file_name);
160+
return false;
161+
}
162+
148163
//FURI_LOG_I(TAG, "Listening at \033[0;33m%s\033[0m.", furi_string_get_cstr(file_name));
149164

150165
subghz->decode_raw_file_worker_encoder = subghz_file_encoder_worker_alloc();
@@ -160,6 +175,7 @@ bool subghz_scene_decode_raw_start(SubGhz* subghz) {
160175

161176
if(!success) {
162177
subghz_file_encoder_worker_free(subghz->decode_raw_file_worker_encoder);
178+
subghz->decode_raw_file_worker_encoder = NULL;
163179
}
164180
}
165181

@@ -190,6 +206,14 @@ bool subghz_scene_decode_raw_next(SubGhz* subghz) {
190206
subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateLoaded);
191207
subghz->state_notifications = SubGhzNotificationStateIDLE;
192208

209+
if(subghz->decode_raw_file_worker_encoder) {
210+
if(subghz_file_encoder_worker_is_running(subghz->decode_raw_file_worker_encoder)) {
211+
subghz_file_encoder_worker_stop(subghz->decode_raw_file_worker_encoder);
212+
}
213+
subghz_file_encoder_worker_free(subghz->decode_raw_file_worker_encoder);
214+
subghz->decode_raw_file_worker_encoder = NULL;
215+
}
216+
193217
subghz_view_receiver_add_data_progress(subghz->subghz_receiver, "Done!");
194218
return false; // No more samples available
195219
}
@@ -269,10 +293,13 @@ bool subghz_scene_decode_raw_on_event(void* context, SceneManagerEvent event) {
269293

270294
subghz_txrx_set_rx_callback(subghz->txrx, NULL, subghz);
271295

272-
if(subghz_file_encoder_worker_is_running(subghz->decode_raw_file_worker_encoder)) {
273-
subghz_file_encoder_worker_stop(subghz->decode_raw_file_worker_encoder);
296+
if(subghz->decode_raw_file_worker_encoder) {
297+
if(subghz_file_encoder_worker_is_running(subghz->decode_raw_file_worker_encoder)) {
298+
subghz_file_encoder_worker_stop(subghz->decode_raw_file_worker_encoder);
299+
}
300+
subghz_file_encoder_worker_free(subghz->decode_raw_file_worker_encoder);
301+
subghz->decode_raw_file_worker_encoder = NULL;
274302
}
275-
subghz_file_encoder_worker_free(subghz->decode_raw_file_worker_encoder);
276303

277304
subghz->state_notifications = SubGhzNotificationStateIDLE;
278305
scene_manager_set_scene_state(

applications/main/subghz/scenes/subghz_scene_read_raw.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,20 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
152152
if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) ||
153153
(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) {
154154
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit);
155-
if(subghz_scene_read_raw_update_filename(subghz)) {
155+
FuriString* temp_raw_path = furi_string_alloc();
156+
furi_string_printf(
157+
temp_raw_path,
158+
"%s/%s%s",
159+
SUBGHZ_RAW_FOLDER,
160+
RAW_FILE_NAME,
161+
SUBGHZ_APP_FILENAME_EXTENSION);
162+
if(subghz_scene_read_raw_update_filename(subghz) &&
163+
furi_string_equal(subghz->file_path, temp_raw_path)) {
156164
furi_string_set(subghz->file_path_tmp, subghz->file_path);
157165
} else {
158166
furi_string_reset(subghz->file_path_tmp);
159167
}
168+
furi_string_free(temp_raw_path);
160169
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
161170
} else {
162171
//Restore default setting
@@ -231,7 +240,10 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
231240
//start send
232241
subghz->state_notifications = SubGhzNotificationStateIDLE;
233242
if(!subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) {
234-
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);
243+
if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRAWLoad &&
244+
subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateRAWMore) {
245+
subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack);
246+
}
235247
subghz_read_raw_set_status(
236248
subghz->subghz_read_raw,
237249
SubGhzReadRAWStatusIDLE,

applications/main/subghz/scenes/subghz_scene_transmitter.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,11 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
129129
int32_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult();
130130
furi_hal_subghz_set_rolling_counter_mult(0);
131131
// Calling restore!
132-
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
132+
subghz_txrx_tx_start(subghz->txrx, subghz_txrx_get_fff_data(subghz->txrx));
133133
subghz_txrx_stop(subghz->txrx);
134134
// Calling restore 2nd time special for FAAC SLH!
135135
// TODO: Find better way to restore after custom button is used!!!
136-
subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx));
136+
subghz_txrx_tx_start(subghz->txrx, subghz_txrx_get_fff_data(subghz->txrx));
137137
subghz_txrx_stop(subghz->txrx);
138138
furi_hal_subghz_set_rolling_counter_mult(tmp_counter);
139139
}

applications/main/subghz/subghz_history.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
#define SUBGHZ_HISTORY_MAX 65535 // uint16_t index max, ram limit below
88
// #define SUBGHZ_HISTORY_FREE_HEAP (23624 * (1 - MIN(rpc_get_sessions_count(instance->rpc), 1U)))
9-
#define SUBGHZ_HISTORY_FREE_HEAP (4 * 1024)
9+
#define SUBGHZ_HISTORY_FREE_HEAP (8 * 1024)
1010

1111
#define TAG "SubGhzHistory"
1212

applications/main/subghz/subghz_i.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "assets_icons.h"
44
#include "subghz/types.h"
55
#include <furi.h>
6+
#include <furi/core/memmgr_heap.h>
67
#include <notification/notification.h>
78
#include <notification/notification_messages.h>
89
#include <flipper_format/flipper_format.h>
@@ -33,6 +34,19 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) {
3334
subghz_dialog_message_freq_error(subghz, can_tx);
3435
break;
3536

37+
case SubGhzTxRxStartTxStateErrorMemory: {
38+
FuriString* msg = furi_string_alloc();
39+
furi_string_printf(
40+
msg,
41+
"Not enough memory\nFree %u B, block %u B\nNeed %u B",
42+
(unsigned)memmgr_get_free_heap(),
43+
(unsigned)memmgr_heap_get_max_free_block(),
44+
(unsigned)subghz_txrx_get_tx_min_heap_required(subghz->txrx));
45+
dialog_message_show_storage_error(subghz->dialogs, furi_string_get_cstr(msg));
46+
furi_string_free(msg);
47+
break;
48+
}
49+
3650
default:
3751
return true;
3852
break;

lib/subghz/protocols/keeloq.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ void* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment) {
121121
instance->keystore = subghz_environment_get_keystore(environment);
122122

123123
instance->encoder.repeat = 3;
124-
instance->encoder.size_upload = 1100;
125-
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
124+
instance->encoder.size_upload = 0;
125+
instance->encoder.upload = NULL;
126126
instance->encoder.is_running = false;
127127

128128
instance->manufacture_from_file = furi_string_alloc();
@@ -643,13 +643,17 @@ static bool
643643
instance->encoder.size_upload = 0;
644644
size_t upindex = 0;
645645

646-
// if we change counter/button in SignalSettings menu then we must bypass counter_modes, just gen and save signal file.
647646
if(subghz_block_generic_global.cnt_need_override ||
648647
subghz_block_generic_global.btn_need_override)
649648
bypass = true;
650649

651-
// Create mode7 upload only if counter and button was not changed by SignalSettings menu
652-
if(keeloq_counter_mode == 7 && !bypass) {
650+
const bool mode7 = (keeloq_counter_mode == 7 && !bypass);
651+
const size_t frame_max = 11 * 2 + 2 + (size_t)instance->generic.data_count_bit * 2 + 2 + 2;
652+
const size_t need = frame_max * (mode7 ? 7 : 1);
653+
if(instance->encoder.upload) free(instance->encoder.upload);
654+
instance->encoder.upload = malloc(need * sizeof(LevelDuration));
655+
656+
if(mode7) {
653657
uint16_t temp_cnt = instance->generic.cnt;
654658
instance->encoder.repeat = 1;
655659
for(uint8_t i = 7; i > 0; i--) {
@@ -672,6 +676,7 @@ static bool
672676
upindex = subghz_protocol_encoder_keeloq_encode_to_timings(
673677
instance, (uint8_t)0x00, true, upindex);
674678
}
679+
furi_check(upindex <= need);
675680
instance->encoder.size_upload = upindex;
676681
return true;
677682
} else {
@@ -680,6 +685,7 @@ static bool
680685
subghz_protocol_encoder_keeloq_encode_to_timings(instance, btn, true, upindex);
681686
}
682687

688+
furi_check(instance->encoder.size_upload <= need);
683689
return true;
684690
}
685691

lib/subghz/protocols/raw.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77

88
#include <flipper_format/flipper_format_i.h>
99
#include <lib/toolbox/stream/stream.h>
10+
#include <furi/core/memmgr.h>
11+
#include <furi/core/memmgr_heap.h>
1012

1113
#define TAG "SubGhzProtocolRaw"
1214

15+
#define SUBGHZ_RAW_TX_MIN_HEAP (7u * 1024u)
16+
1317
#define SUBGHZ_DOWNLOAD_MAX_SIZE 512
1418

1519
static const SubGhzBlockConst subghz_protocol_raw_const = {
@@ -320,6 +324,12 @@ static bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* in
320324
furi_assert(instance);
321325
furi_check(!instance->file_worker_encoder);
322326

327+
if(memmgr_get_free_heap() < SUBGHZ_RAW_TX_MIN_HEAP ||
328+
memmgr_heap_get_max_free_block() < SUBGHZ_RAW_TX_MIN_HEAP) {
329+
FURI_LOG_E(TAG, "Not enough memory to start TX");
330+
return false;
331+
}
332+
323333
instance->file_worker_encoder = subghz_file_encoder_worker_alloc();
324334
if(subghz_file_encoder_worker_start(
325335
instance->file_worker_encoder,

0 commit comments

Comments
 (0)