If statement behaves erratically
Not only must cnt variable be declared volatile, but accessing it from the main loop must also be protected because:


it can be modified by an ISR
it is longer than 1 byte


In AVR architecture (8-bits), read/write operations on bytes are guaranteed to be atomic, i.e. no function can access a byte while it is read/written by an ISR.

However, operations on types larger than bytes are not atomic!

This means that reading e.g. an int variable from your main loop might be interrupted in the middle of the operation (only 1 byte read) by an ISR that could modify the value (both bytes this time); then when the ISR is finished the main loop would resume reading the second byte -modified by the ISR- of the variable, while keeping the first original byte -not modified by the ISR.

I would advise you to write a pair of functions to read and write cnt in a protected way, i.e. with interrupts disabled:

volatile int cnt;

void set_cnt(int val) {
    uint8_t status = SREG;
    cli();
    cnt = val;
    SREG = status;
}

int get_cnt() {
    uint8_t status = SREG;
    cli();
    int val = cnt;
    SREG = status;
    return val;
}


What those functions do are:


get the current status of exceptions (from SREG register)
disable exceptions with cli()
access cnt without any risk of it being accessed concurrently
restore initial status of exceptions (to SREG register)


Everywhere in your code (except in code called from an ISR where it is not necessary), you must then call these functions to access cnt variable.