Perchè metti !negato..
Probabilmente l'ora a cui ho scritto il post mi ha fatto da tara!
Io di solito uso preg_match() per questo tipo di riconoscimenti e funziona al contrario: alla prima occorrenza, la funzione torna FALSE.
Ho scritto il codice al volo e non l'ho testato.

però può capitare anche che si manifestano piu errori .. in questo caso hai messo exit() nell if che controlla l'username... però se si manifesta anche per l'email? e poi se il campo p vuoto?
Si, però te lanciavi l'header con una sintassi sbagliata per "trasportarci" l'errore: il $_GET come lo avevi messo te avrebbe tornato una stringa vuota.
Avevo corretto per come reputo sia più logico e sintatticamente corretto fare.
Peraltro, anche nel tuo codice, al primo errore viene lanciato il redirect, bloccando di fatto il resto dello script.... ed è anche una cosa logica: che senso ha continuare nei controlli di tutti gli input se tanto uno presenta dei caratteri che te non vuoi???

e poi se il campo p vuoto? io avevo messo anche un contrllo nel momento in cui il campo fosse vuoto...
Il controllo come lo fai nel tuo primo script non ha troppo senso ed ora ti spiego perché.
Se un utente, preme 1, 2 o più volte la barra spaziatrice sappi che il campo non sarà mai vuoto. In poche parole: lo spazio è un carattere particolare che non viene stampato a video, ma che è sempre e comunque lì.

Dovresti aggiungere trim() nella funzione control_char() e poi controllare nell'IF se la variabile è valorizzata con empty().
Ti riscrivo il codice.

Codice PHP:
function control_char($str) { 
    
trim($str);
    return (bool) 
eregi('[*<>&%"#[]'$str); 
}

/* 
   RESTO DEL CODICE
*/

if(control_char($username) || empty(control_char($username))) { 
    
header ("Location: login_failed.php"); 
    exit; 
}
/* 
   STESSO CONTROLLO PER LA PASSWORD
*/ 
Avrai notato che non ho aggiunto il trim() all'altra funzione, perché tanto se non viene rispettata la sintassi di un indirizzo email torna comunque errore.

Spero ti sia più chiaro adesso.