Skip to content

fix: Prevent possible infinite loop with invalid smtp row#7746

Merged
Hocuri merged 1 commit intomainfrom
hoc/no-early-return-in-smtp-send
Jan 21, 2026
Merged

fix: Prevent possible infinite loop with invalid smtp row#7746
Hocuri merged 1 commit intomainfrom
hoc/no-early-return-in-smtp-send

Conversation

@Hocuri
Copy link
Copy Markdown
Collaborator

@Hocuri Hocuri commented Jan 19, 2026

If Message::load_from_db_optional() or set_msg_failed() fails, we shouldn't early-return. Because it's important that the line execute("DELETE FROM smtp WHERE id=?", (rowid,)) is executed in order to prevent an infinite loop, if one of these functions fails.

@Hocuri Hocuri requested review from iequidoo and link2xt January 19, 2026 14:39
Copy link
Copy Markdown
Collaborator

@link2xt link2xt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just move the DELETE FROM smtp above?

@Hocuri
Copy link
Copy Markdown
Collaborator Author

Hocuri commented Jan 19, 2026

We can, but why? It seems weird to abort sending messages, early-return from send_smtp_messages(), and wait 30s, because we didn't manage to mark a message as failed.

Or do you mean, in addition to what I already did in the PR here?

@link2xt
Copy link
Copy Markdown
Collaborator

link2xt commented Jan 19, 2026

It seems weird to abort sending messages, early-return from send_smtp_messages(), and wait 30s, because we didn't manage to mark a message as failed.

If we fail to mark the message as failed there is something really wrong with the database or the filesystem or hardware. Ignoring errors everywhere just adds more untested codepaths.

We also just got another one case in another PR, also making more codepaths:
#7738 (comment)

@iequidoo
Copy link
Copy Markdown
Collaborator

iequidoo commented Jan 20, 2026

Can we just move the DELETE FROM smtp above?

I agree that in this particular function it's better to move DELETE FROM smtp above. If we want to ignore some errors in the SMTP loop, then better to do that directly in that loop.

EDIT: And send_msg_to_smtp() is a quite complex function, so we can't predict all scenarios when it can fail and it's better to ignore the error if it fails for a particular message.

Make sure that the row is deleted from SMTP even if we didn't manage to
mark the message as failed
@Hocuri Hocuri force-pushed the hoc/no-early-return-in-smtp-send branch from 50481bf to aa6b8e9 Compare January 20, 2026 08:52
@Hocuri
Copy link
Copy Markdown
Collaborator Author

Hocuri commented Jan 20, 2026

Early-returning with ? also adds another code path, but it's not really important. I just moved the DELETE, so, the infinite loop is definitely fixed now.

@Hocuri Hocuri requested a review from link2xt January 20, 2026 08:55
@iequidoo
Copy link
Copy Markdown
Collaborator

I think that the real problem isn't "new code paths", but rather a state when a message remains in InPending without any SMTP rows, but this shouldn't happen unless there's an app crash or so.

@link2xt
Copy link
Copy Markdown
Collaborator

link2xt commented Jan 21, 2026

Early-returning with ? also adds another code path

If you have a code that looks like this:

foo()?;
bar()?;
baz()?;

then you have 4 possible paths: foo fails, bar fails, baz fails or everything succeeds.

If you ignore errors like this, you get 8 possible code paths:

foo().log_err().ok();
bar().log_err().ok();
baz().log_err().ok();

@Hocuri Hocuri merged commit a6b2a54 into main Jan 21, 2026
30 checks passed
@Hocuri Hocuri deleted the hoc/no-early-return-in-smtp-send branch January 21, 2026 15:46
@iequidoo
Copy link
Copy Markdown
Collaborator

iequidoo commented Jan 21, 2026

If the "failed" branches are independent from each other (there's no correlation probabilistically), it's enough to have 4 tests -- everything succeeds, foo() fails, bar() fails, baz() fails -- to cover the code. The same amount of tests is needed if we early return with ?. Ok, skipped errors may add side effects, but they also happen if other async tasks are run in parallel. So i think there's no generic rule, reducing number of code paths may or may not make sense depending on the case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants