Given
assert(*m > 0);
assert(*d > 0);
at the end of normalize_y_m_d(), it looks like at lest 1 <=*month and 1 <=*day are redundant.
A closer look also reveals
assert(1 <= *m && *m <= 12);
in the middle of normalize_y_m_d(). This seems to leave only *day <=31 possibly relevant.
I suspect that out of bounds day surviving normalize_y_m_d() is a logical error in that function that needs to be fixed and an assert() added at the end. The proposed patch appears to cure the symptom rather than the actual flaw.

I am attaching my variant of the patch including additional unit tests.
Note that my changes make normalize_y_m_d() and normalize_date() the same, but I am leaving the current structure intact to make reviewer's job easier.

As an aside, I dislike the fact that the datetime module uses a C 'int' for date ordinals, and clearly assumes that it'll be at least 32 bits. int could be as small as 16 bits on some systems (small embedded systems?). But that's another issue.