Why this code causes infinite loop?

It was correctly pointed out what the issue is, but I also want to explain it so you have better idea of mechanics:

  • when you use query_posts() it writes down generated WP_Query object into global $wp_query variable;

  • have_posts() and the_post() functions are wrappers for methods of same name called using global $wp_query object;

  • so what you are doing in that code is keep overwriting $wp_query and keep asking it if there are posts to process. And there are always posts because as soon as you process post you kick query back to new clean state.

Also see When should you use WP_Query vs query_posts() vs get_posts()?