alliedmodders/amxmodx — pull request #140 by Nextra (2014-10-10 22:51:07+00:00)
Title: Fix recursion issues with show_menu and newmenus

#21 fixed a bug where calling ``show_menu`` would not fire the newmenu callback if the client was already viewing a newmenu before. Firing this callback during ``show_menu`` can result in a possible recursion if the callback function also attempts to open an old menu (which is possible if the plugin mixes old and new menu styles).

Doing this causes trouble as the recursing ``show_menu`` call will modify the static buffer used by ``get_amxstring``. The changed buffer content will therefore contain wrong content and can - more importantly - cause the ``ilen`` variable to not match the string length. ``UTIL_ShowMenu`` will then run into an infinite loop and lock up the server.

This change moves the code closing the currently viewed menu to the very top of the ``show_menu`` native before ``get_amxstring`` is used, avoiding the issue.

Reported here: https://forums.alliedmods.net/showthread.php?t=249671

Repro & test plugin:
```SourcePawn
#include
#include

#pragma semicolon 1
#pragma ctrlchar '\'

public plugin_init() {
register_plugin("Menu Recursion", "0.0.1", "Nextra");

register_clcmd("newmenu", "cmd_newmenu");
register_clcmd("oldmenu", "cmd_oldmenu");

register_menu("Test Old Menu", 1023, "oldmenu_handle");
}

public cmd_newmenu(const client) {
show_newmenu(client);
return PLUGIN_HANDLED;
}

show_newmenu(const client) {
new menu = menu_create("Test New Menu", "newmenu_handle");
menu_additem(menu, "Test Item");
menu_display(client, menu);
}

public newmenu_handle(const client, const menu, const item) {
if (item == MENU_EXIT) {
client_print(client, print_chat, "Newmenu Close");
show_oldmenu(client, 1);
}

menu_destroy(menu);
return PLUGIN_HANDLED;
}

public cmd_oldmenu(const client) {
show_oldmenu(client, 0);
return PLUGIN_HANDLED;
}

show_oldmenu(const client, const i) {
if (i) {
show_menu(client, 1023, "\\yBreak Old Menu\n\n\\r1.\\w Test Item\n\n\\r0.\\w Exit", _, "Test Old Menu");
} else {
show_menu(client, 1023, "\\yTest Old Menu\n\n\\r1.\\w Test Item\n\n\\r0.\\w Exit", _, "Test Old Menu");
}
}

public oldmenu_handle(const client, const key) {
return PLUGIN_HANDLED;
}
```