SELECT gives different results with/without FOR UPDATE in same transaction
A guess:

"Repeatable Read" means that the identical SELECT will produce identical results throughout the transaction.  But, apparently, the FOR UPDATE clause is enough to make the two selects not 'identical'.

Under the covers...  There is a clock (sequence number) ticking away.  Whenever a row is modified, it is copied over.  Now there are two copies of that row, one with the old clock, one with the new.  (See "History list".)  Your first SELECT is associated with the older clock value; the second SELECT, not being identical to the first, has a newer clock value, hence linked to a different copy of the row.

After the transaction if finished (COMMIT or ROLLBACK), the extra copy of the row is tossed.  (I think this is a background task for COMMIT, but a "do it now" task for ROLLBACK.)  Meanwhile, other connections pick the appropriate row from the history list for each row based on their isolation_mode.  Roughly speaking, READ_COMMITTED goes to the old end of the list; READ_UNCOMMITTED ("dirty read") goes to the new end.

Things are optimized on the "optimistic" assumption that COMMITs are more likely than ROLLBACKs.  Hence, the comments in the previous paragraph, and the slowness of ROLLBACK.