Skip to content

Commit 0ed4593

Browse files
committed
Fix use-after-free by keyboard commands when connection/controller is being shut down.
1 parent 154bcf3 commit 0ed4593

File tree

9 files changed

+50
-11
lines changed

9 files changed

+50
-11
lines changed

SerialPrograms/Source/Controllers/KeyboardInput/KeyboardInput.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@ void KeyboardInputController::start(){
2727
[this]{ thread_loop(); }
2828
);
2929
}
30-
void KeyboardInputController::stop(){
31-
m_stop.store(true, std::memory_order_release);
30+
void KeyboardInputController::stop() noexcept{
31+
bool expected = false;
32+
if (!m_stop.compare_exchange_strong(expected, true)){
33+
// Already stopped.
34+
return;
35+
}
3236
{
3337
std::lock_guard<std::mutex> lg(m_sleep_lock);
3438
m_cv.notify_all();

SerialPrograms/Source/Controllers/KeyboardInput/KeyboardInput.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ class KeyboardInputController{
5151

5252

5353
protected:
54-
void start(); // Child class must call this at end of constructor.
55-
void stop(); // Child class must call this at start of destructor.
54+
void start(); // Child class must call this at end of constructor.
55+
void stop() noexcept; // Child class must call this at start of destructor.
5656

5757
virtual std::unique_ptr<ControllerState> make_state() const = 0;
5858
virtual void update_state(ControllerState& state, const std::set<uint32_t>& pressed_keys) = 0;

SerialPrograms/Source/Controllers/SerialPABotBase/SerialPABotBase_Connection.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ void SerialPABotBase_Connection::thread_body(){
304304
if (!m_ready.load(std::memory_order_relaxed)){
305305
break;
306306
}
307+
307308
m_cv.wait_for(lg, SERIAL_REFRESH_RATE);
308309
}
309310
});

SerialPrograms/Source/NintendoSwitch/Controllers/NintendoSwitch_ProController.cpp

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class ProController::KeyboardManager : public KeyboardInputController{
3232
public:
3333
KeyboardManager(ProController& controller)
3434
: KeyboardInputController(true)
35-
, m_controller(controller)
35+
, m_controller(&controller)
3636
{
3737
std::vector<std::shared_ptr<EditableTableRow>> mapping =
3838
ConsoleSettings::instance().KEYBOARD_MAPPINGS.TABLE.current_refs();
@@ -45,6 +45,16 @@ class ProController::KeyboardManager : public KeyboardInputController{
4545
~KeyboardManager(){
4646
stop();
4747
}
48+
void stop() noexcept{
49+
{
50+
WriteSpinLock lg(m_lock);
51+
if (m_controller == nullptr){
52+
return;
53+
}
54+
m_controller = nullptr;
55+
}
56+
KeyboardInputController::stop();
57+
}
4858

4959
virtual std::unique_ptr<ControllerState> make_state() const override{
5060
return std::make_unique<SwitchControllerState>();
@@ -65,10 +75,18 @@ class ProController::KeyboardManager : public KeyboardInputController{
6575
deltas.to_state(static_cast<SwitchControllerState&>(state));
6676
}
6777
virtual void cancel_all_commands() override{
68-
m_controller.cancel_all_commands();
78+
WriteSpinLock lg(m_lock);
79+
if (m_controller == nullptr){
80+
return;
81+
}
82+
m_controller->cancel_all_commands();
6983
}
7084
virtual void replace_on_next_command() override{
71-
m_controller.replace_on_next_command();
85+
WriteSpinLock lg(m_lock);
86+
if (m_controller == nullptr){
87+
return;
88+
}
89+
m_controller->replace_on_next_command();
7290
}
7391
virtual void send_state(const ControllerState& state) override{
7492
const SwitchControllerState& switch_state = static_cast<const SwitchControllerState&>(state);
@@ -82,7 +100,11 @@ class ProController::KeyboardManager : public KeyboardInputController{
82100
COLOR_DARKGREEN
83101
);
84102
#endif
85-
m_controller.issue_full_controller_state(
103+
WriteSpinLock lg(m_lock);
104+
if (m_controller == nullptr){
105+
return;
106+
}
107+
m_controller->issue_full_controller_state(
86108
nullptr,
87109
switch_state.buttons,
88110
switch_state.dpad,
@@ -96,7 +118,8 @@ class ProController::KeyboardManager : public KeyboardInputController{
96118

97119

98120
private:
99-
ProController& m_controller;
121+
SpinLock m_lock;
122+
ProController* m_controller;
100123
std::map<Qt::Key, ControllerDeltas> m_mapping;
101124
};
102125

@@ -111,6 +134,9 @@ ProController::ProController(Milliseconds timing_variation)
111134
{
112135

113136
}
137+
void ProController::stop() noexcept{
138+
m_keyboard_manager->stop();
139+
}
114140

115141

116142
void ProController::keyboard_release_all(){

SerialPrograms/Source/NintendoSwitch/Controllers/NintendoSwitch_ProController.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ class ProController : public AbstractController{
8484
virtual ~ProController();
8585
ProController(Milliseconds timing_variation);
8686

87+
// Must call before destruction begins.
88+
void stop() noexcept;
89+
8790
virtual Logger& logger() = 0;
8891

8992
Milliseconds timing_variation() const{

SerialPrograms/Source/NintendoSwitch/Controllers/NintendoSwitch_ProController_SerialPABotBase.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ ProController_SerialPABotBase::ProController_SerialPABotBase(
6767

6868
m_error_string = html_color_text("Missing Feature: " + missing_feature, COLOR_RED);
6969
}
70+
ProController_SerialPABotBase::~ProController_SerialPABotBase(){
71+
stop();
72+
}
7073

7174

7275

SerialPrograms/Source/NintendoSwitch/Controllers/NintendoSwitch_ProController_SerialPABotBase.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace PokemonAutomation{
1616
namespace NintendoSwitch{
1717

1818

19-
class ProController_SerialPABotBase : public ProControllerWithScheduler{
19+
class ProController_SerialPABotBase final : public ProControllerWithScheduler{
2020
public:
2121
using ContextType = ProControllerContext;
2222

@@ -26,6 +26,7 @@ class ProController_SerialPABotBase : public ProControllerWithScheduler{
2626
SerialPABotBase::SerialPABotBase_Connection& connection,
2727
const ControllerRequirements& requirements
2828
);
29+
~ProController_SerialPABotBase();
2930

3031
virtual bool is_ready() const override{
3132
return m_serial && m_handle.is_ready() && m_error_string.empty();

SerialPrograms/Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase_ProController.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ ProController_SysbotBase::ProController_SysbotBase(
6969

7070
}
7171
ProController_SysbotBase::~ProController_SysbotBase(){
72+
stop();
7273
m_stopping.store(true, std::memory_order_release);
7374
{
7475
std::lock_guard<std::mutex> lg(m_state_lock);

SerialPrograms/Source/NintendoSwitch/Controllers/SysbotBase/SysbotBase_ProController.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace NintendoSwitch{
1919

2020

2121

22-
class ProController_SysbotBase : public ProControllerWithScheduler{
22+
class ProController_SysbotBase final : public ProControllerWithScheduler{
2323
public:
2424
using ContextType = ProControllerContext;
2525

0 commit comments

Comments
 (0)