Function smtp_server_connection_handle_command
in src/lib-smtp/smtp-server-connection.c creates a variable named cmd with
cmd = smtp_server_command_new(tmp_conn, cmd_name, cmd_params);
It gets used with return (cmd == NULL || !cmd->input_locked);
ie cmd->input_locked
dereferences the pointer
But we can get to this code with cmd != NULL and cmd was freed
To do so, we can trigger in smtp_server_command_submit_reply
the condition
if (conn != NULL && conn->bad_counter > conn->set.max_bad_commands) {
which calls smtp_server_connection_terminate
which ends up freeing cmd (smtp_server_connection_close
, smtp_server_connection_disconnect
, smtp_server_command_unref
)
To do so, it is enough do send enough linefeeds characters like “\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n” to the server.
To me the flawed logic is to have the check if (!smtp_server_connection_unref(&tmp_conn)) {
This decreases reference count only once, which has been increased before at least twice in smtp_server_connection_handle_command
and its caller smtp_server_connection_input
Proposed patch is
diff --git a/src/lib-smtp/smtp-server-connection.c b/src/lib-smtp/smtp-server-connection.c
index 404acc888b..ab8746fb2b 100644
--- a/src/lib-smtp/smtp-server-connection.c
+++ b/src/lib-smtp/smtp-server-connection.c
@@ -295,8 +295,10 @@ smtp_server_connection_handle_command(struct smtp_server_connection *conn,
struct smtp_server_command *cmd;
smtp_server_connection_ref(tmp_conn);
+ int refcount = tmp_conn->refcount;
cmd = smtp_server_command_new(tmp_conn, cmd_name, cmd_params);
- if (!smtp_server_connection_unref(&tmp_conn)) {
+ if (refcount > tmp_conn->refcount ||
+ !smtp_server_connection_unref(&tmp_conn)) {
/* the command start callback managed to get this connection
destroyed */
return FALSE;
This was found by fuzzing
The attacker can use this as a denial of service against the SMTP server.