File 0697-erts-Fix-use-after-free-for-send-delay-write-error.patch of Package erlang
From baaa8f30d782a4b79828b84fcd6c0e92d47ac6c0 Mon Sep 17 00:00:00 2001
From: Lukas Larsson <lukas@erlang.org>
Date: Wed, 13 Oct 2021 09:52:58 +0200
Subject: [PATCH] erts: Fix use after free for send-delay write error
If a socket was in active mode and a write error happened
when a send_delay was triggered the driver would read data
from the deallocated tcp_description and crash.
---
erts/emulator/drivers/common/inet_drv.c | 38 ++++++++++++++-----------
lib/kernel/test/gen_tcp_misc_SUITE.erl | 10 +++++--
2 files changed, 28 insertions(+), 20 deletions(-)
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index b69906b3e1..46e73c8b83 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -1039,26 +1039,26 @@ typedef union {
(1 + 2 + 4) : localaddrlen(data))
#endif
+typedef int (MultiTimerFunction)(ErlDrvData drv_data, ErlDrvTermData caller);
+
typedef struct _multi_timer_data {
ErlDrvTime when;
ErlDrvTermData caller;
- void (*timeout_function)(ErlDrvData drv_data, ErlDrvTermData caller);
+ MultiTimerFunction *timeout_function;
struct _multi_timer_data *next;
struct _multi_timer_data *prev;
} MultiTimerData;
static MultiTimerData *add_multi_timer(tcp_descriptor *desc, ErlDrvPort port,
ErlDrvTermData caller, unsigned timeout,
- void (*timeout_fun)(ErlDrvData drv_data,
- ErlDrvTermData caller));
+ MultiTimerFunction *timeout_fun);
static void fire_multi_timers(tcp_descriptor *desc, ErlDrvPort port,
ErlDrvData data);
static void remove_multi_timer(tcp_descriptor *desc, ErlDrvPort port, MultiTimerData *p);
static void cancel_multi_timer(tcp_descriptor *desc, ErlDrvPort port,
- void (*timeout_fun)(ErlDrvData drv_data,
- ErlDrvTermData caller));
+ MultiTimerFunction *timeout_fun);
-static void tcp_inet_multi_timeout(ErlDrvData e, ErlDrvTermData caller);
+static int tcp_inet_multi_timeout(ErlDrvData e, ErlDrvTermData caller);
static void clean_multi_timers(tcp_descriptor *desc, ErlDrvPort port);
typedef struct {
@@ -10131,13 +10131,14 @@ static void tcp_desc_close(tcp_descriptor* desc)
erl_inet_close(INETP(desc));
}
-static void tcp_inet_recv_timeout(ErlDrvData e, ErlDrvTermData dummy)
+static int tcp_inet_recv_timeout(ErlDrvData e, ErlDrvTermData dummy)
{
tcp_descriptor* desc = (tcp_descriptor*)e;
ASSERT(!desc->inet.active);
sock_select(INETP(desc),(FD_READ|FD_CLOSE),0);
desc->i_remain = 0;
async_error_am(INETP(desc), am_timeout);
+ return 1;
}
/* TCP requests from Erlang */
@@ -10554,7 +10555,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
}
-static void tcp_inet_send_timeout(ErlDrvData e, ErlDrvTermData dummy)
+static int tcp_inet_send_timeout(ErlDrvData e, ErlDrvTermData dummy)
{
tcp_descriptor* desc = (tcp_descriptor*)e;
ASSERT(IS_BUSY(INETP(desc)));
@@ -10567,6 +10568,7 @@ static void tcp_inet_send_timeout(ErlDrvData e, ErlDrvTermData dummy)
if (desc->send_timeout_close) {
tcp_desc_close(desc);
}
+ return 1;
/* Q: Why not keep port busy as send queue may still be full (ERL-1390)?
*
* A: If kept busy, a following send call would hang without a timeout
@@ -10616,14 +10618,14 @@ static void tcp_inet_timeout(ErlDrvData e)
DEBUGF(("tcp_inet_timeout(%p) }\r\n", desc->inet.port));
}
-static void tcp_inet_multi_timeout(ErlDrvData e, ErlDrvTermData caller)
+static int tcp_inet_multi_timeout(ErlDrvData e, ErlDrvTermData caller)
{
tcp_descriptor* desc = (tcp_descriptor*)e;
int id,req;
ErlDrvMonitor monitor;
if (remove_multi_op(desc, &id, &req, caller, NULL, &monitor) != 0) {
- return;
+ return 1;
}
driver_demonitor_process(desc->inet.port, &monitor);
if (desc->multi_first == NULL) {
@@ -10631,6 +10633,7 @@ static void tcp_inet_multi_timeout(ErlDrvData e, ErlDrvTermData caller)
desc->inet.state = INET_STATE_LISTENING; /* restore state */
}
send_async_error(desc->inet.dport, id, caller, am_timeout);
+ return 1;
}
@@ -11605,10 +11608,10 @@ static int tcp_shutdown_error(tcp_descriptor* desc, int err)
return tcp_send_or_shutdown_error(desc, err);
}
-static void tcp_inet_delay_send(ErlDrvData data, ErlDrvTermData dummy)
+static int tcp_inet_delay_send(ErlDrvData data, ErlDrvTermData dummy)
{
tcp_descriptor *desc = (tcp_descriptor*)data;
- (void)tcp_inet_output(desc, (HANDLE) INETP(desc)->s);
+ return tcp_inet_output(desc, (HANDLE) INETP(desc)->s);
}
/*
@@ -13320,7 +13323,10 @@ static void fire_multi_timers(tcp_descriptor *desc, ErlDrvPort port,
if (desc->mtd != NULL)
desc->mtd->prev = NULL;
- (*(save.timeout_function))(data,save.caller);
+ if ((*(save.timeout_function))(data,save.caller) < 0)
+ /* -1 means tcp_descriptor has been deallocated,
+ so we have to return as soon as possible */
+ return;
if (desc->mtd == NULL)
return;
@@ -13372,8 +13378,7 @@ static void remove_multi_timer(tcp_descriptor *desc, ErlDrvPort port, MultiTimer
/* Cancel a timer based on the timeout_fun */
static void cancel_multi_timer(tcp_descriptor *desc, ErlDrvPort port,
- void (*timeout_fun)(ErlDrvData drv_data,
- ErlDrvTermData caller))
+ MultiTimerFunction *timeout_fun)
{
MultiTimerData *timer = desc->mtd;
while(timer && timer->timeout_function != timeout_fun) {
@@ -13386,8 +13391,7 @@ static void cancel_multi_timer(tcp_descriptor *desc, ErlDrvPort port,
static MultiTimerData *add_multi_timer(tcp_descriptor *desc, ErlDrvPort port,
ErlDrvTermData caller, unsigned timeout,
- void (*timeout_fun)(ErlDrvData drv_data,
- ErlDrvTermData caller))
+ MultiTimerFunction *timeout_fun)
{
MultiTimerData *mtd, *p, *s;
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 9f23ca5992..e6d875ca8d 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -6563,11 +6563,15 @@ delay_send_error(Config) ->
?LISTEN(Config,
0, [{reuseaddr, true}, {packet, 1}, {active, false}]),
{ok,{{0,0,0,0},PortNum}}=inet:sockname(L),
- ?P("try connect - with delay_send:true"),
+
+ delay_send_error(Config, L, PortNum, false),
+ delay_send_error(Config, L, PortNum, true).
+delay_send_error(Config, L, PortNum, Active) ->
+ ?P("try connect - with delay_send:true active:~p",[Active]),
{ok, C} =
?CONNECT(Config,
"localhost", PortNum,
- [{packet, 1}, {active, false}, {delay_send, true}]),
+ [{packet, 1}, {active, Active}, {delay_send, true}]),
?P("try accept"),
{ok, S} = gen_tcp:accept(L),
%% Do a couple of sends first to see that it works
@@ -6579,7 +6583,7 @@ delay_send_error(Config) ->
ok = gen_tcp:send(C, "hello"),
%% Close the receiver
?P("close receiver (accepted socket)"),
- ok = gen_tcp:close(S),
+ ok = gen_tcp:shutdown(C, write),
%%
?P("send data"),
case gen_tcp:send(C, "hello") of
--
2.31.1