CLI clears the interrupt flag (IF = 0): the CPU stops answering requests on its INTR line, which on a PC is everything that comes through the interrupt controller: the timer, the keyboard, disks, serial ports. STI sets IF = 1 again. Requests that arrive meanwhile wait at the controller; nothing is lost unless you keep interrupts off so long that a second tick or key arrives.

IF does not block everything: the non-maskable interrupt (NMI), software INT n, INTO, and the divide error still happen.

Rules of thumb

Keep the window short: the clock stops counting and keys can be dropped. Instead of a bare STI afterwards, consider PUSHF / CLI ... POPF, which restores whatever IF was before, so the code also works when called with interrupts already off. Entering any interrupt handler clears IF, and IRET restores the old flags, so a handler runs with interrupts off unless it executes STI.

The one-instruction delay

After STI the CPU executes one more instruction before it accepts an interrupt. So STI / HLT cannot lose a wakeup between the two, and STI / RET returns before any handler runs. Loading a segment register with MOV or POP holds off interrupts the same way for one instruction, so MOV SS, AX / MOV SP, BX switches the stack safely. Some very early 8088 chips got this wrong, which is why old code wraps stack switches in CLI / STI anyway.

CLI ; IF = 0
PUSHF
POP BX ; the flags, IF off
STI ; IF = 1
MOV AX, BX
MOV CL, 9
SHR AX, CL ; IF is bit 9
AND AL, 1 ; 0
ADD AL, '0' ; digit -> character
MOV DL, AL
MOV AH, 02h ; DOS: print DL
INT 21h
PUSHF
POP AX
MOV CL, 9
SHR AX, CL
AND AL, 1 ; 1: on again
ADD AL, '0' ; digit -> character
MOV DL, AL
MOV AH, 02h ; DOS: print DL
INT 21h

Later CPUs: in protected and virtual-8086 mode CLI / STI are privileged, controlled by the I/O privilege level.