%s in SendClientMessage
#1

Sending "%s" to a client via SendClientMessage crashes the server.

E.g.:
pawn Код:
SendClientMessage(playerid, 0, "%s");
Log on Windows:
Код:
[12:26:37] [debug] Server crashed due to an unknown error
[12:26:37] [debug] System backtrace:
[12:26:38] [debug] #0 00493271 in ?? () from D:\Files\Game servers\SAMP\godfather\samp-server.exe
[12:26:38] [debug] #1 0044e119 in ?? () from D:\Files\Game servers\SAMP\godfather\samp-server.exe
[12:26:38] [debug] #2 0045953c in ?? () from D:\Files\Game servers\SAMP\godfather\samp-server.exe
[12:26:38] [debug] #3 0045bd3a in ?? () from D:\Files\Game servers\SAMP\godfather\samp-server.exe
[12:26:38] [debug] #4 774342e6 in ?? () from C:\Windows\SYSTEM32\ntdll.dll
Log on Linux:
Код:
[12:22:30] [debug] AMX backtrace:
[12:22:30] [debug] #0 native SendClientMessage () [080d75c0] from samp03svr
[12:22:30] [debug] #1 00023574 in public cmd_resp (playerid=8, params[]=@0x000ebc00 "seb") at core/Header.pwn:20
[12:22:30] [debug] #2 0007a428 in public FIXES_OnPlayerCommandText (playerid=8, cmdtext[]=@0x000ebbd8 "/resp seb") at core/Header.pwn:25
[12:22:30] [debug] #3 000010e4 in public OnPlayerCommandText (playerid=8, cmdtext[]=@0x000ebbb0 "/resp seb") at D:\Files\Game servers\SAMP\godfather\pawno\include\fixes.inc:1966
[12:22:30] [debug] System backtrace:
[12:22:30] [debug] #0 f7e66f41 in _ZN10StackTraceC1EPv () from plugins/crashdetect.so
[12:22:30] [debug] #1 f7e5d41f in _ZN11crashdetect20PrintSystemBacktraceEPv () from plugins/crashdetect.so
[12:22:30] [debug] #2 f7e5b963 in _ZN11crashdetect15SystemExceptionEPv () from plugins/crashdetect.so
[12:22:30] [debug] #3 f7e663c1 in ?? () from plugins/crashdetect.so
[12:22:30] [debug] #4 ffffe600 in ?? ()
[12:22:30] [debug] #5 0028020b in strlen () from /lib/libc.so.6
[12:22:30] [debug] #6 00251064 in _IO_vfprintf () from /lib/libc.so.6
[12:22:30] [debug] #7 0026a79c in vsprintf () from /lib/libc.so.6
[12:22:30] [debug] #8 080abe41 in ?? () from ./samp03svr
[12:22:30] [debug] #9 080d7669 in ?? () from ./samp03svr
[12:22:30] [debug] #10 080954a4 in ?? () from ./samp03svr
[12:22:30] [debug] #11 f7e5c5a2 in _ZN11crashdetect13DoAmxCallbackEiPiS0_ () from plugins/crashdetect.so
[12:22:30] [debug] #12 f7e6465e in ?? () from plugins/crashdetect.so
[12:22:30] [debug] #13 f7e6b926 in amx_Exec () from plugins/crashdetect.so
[12:22:30] [debug] #14 f7e5c622 in _ZN11crashdetect9DoAmxExecEPii () from plugins/crashdetect.so
[12:22:30] [debug] #15 f7e64699 in ?? () from plugins/crashdetect.so
[12:22:30] [debug] #16 080dcc22 in ?? () from ./samp03svr
[12:22:30] [debug] #17 f7e6ba33 in amx_Exec () from plugins/crashdetect.so
[12:22:30] [debug] #18 f7e5c622 in _ZN11crashdetect9DoAmxExecEPii () from plugins/crashdetect.so
[12:22:30] [debug] #19 f7e64699 in ?? () from plugins/crashdetect.so
[12:22:30] [debug] #20 080dcc22 in ?? () from ./samp03svr
[12:22:30] [debug] #21 f7e6ba33 in amx_Exec () from plugins/crashdetect.so
[12:22:30] [debug] #22 f7e5c622 in _ZN11crashdetect9DoAmxExecEPii () from plugins/crashdetect.so
[12:22:30] [debug] #23 f7e64699 in ?? () from plugins/crashdetect.so
[12:22:30] [debug] #24 080a4da2 in ?? () from ./samp03svr
[12:22:30] [debug] #25 080afcca in ?? () from ./samp03svr
[12:22:30] [debug] #26 080733b1 in ?? () from ./samp03svr
[12:22:30] [debug] #27 080734a2 in ?? () from ./samp03svr
[12:22:30] [debug] #28 0807d2b0 in ?? () from ./samp03svr
[12:22:30] [debug] #29 080ad426 in ?? () from ./samp03svr
[12:22:30] [debug] #30 080ad652 in ?? () from ./samp03svr
[12:22:30] [debug] #31 080a8fb3 in ?? () from ./samp03svr
[12:22:30] [debug] #32 00225e9c in __libc_start_main () from /lib/libc.so.6
[12:22:30] [debug] #33 0804b491 in ?? () from ./samp03svr
^ The logs above are from my gamemode, but I've tested in with a bare gamemode too.

Код:
#include <a_samp>
#include <core>
#include <float>

#pragma tabsize 0

main()
{
	print("\n----------------------------------");
	print("  Bare Script\n");
	print("----------------------------------\n");
}

public OnPlayerConnect(playerid)
{
	GameTextForPlayer(playerid,"~w~SA-MP: ~r~Bare Script",5000,5);
	return 1;
}

public OnPlayerCommandText(playerid, cmdtext[])
{
	new idx;
	new cmd[256];
	
	cmd = strtok(cmdtext, idx);

	if(strcmp(cmd, "/crash", true) == 0) {
	    SendClientMessage(playerid, 0, "%s");
    	return 1;
	}

	return 0;
}

public OnPlayerSpawn(playerid)
{
	SetPlayerInterior(playerid,0);
	TogglePlayerClock(playerid,0);
	return 1;
}

public OnPlayerDeath(playerid, killerid, reason)
{
   	return 1;
}

SetupPlayerForClassSelection(playerid)
{
 	SetPlayerInterior(playerid,14);
	SetPlayerPos(playerid,258.4893,-41.4008,1002.0234);
	SetPlayerFacingAngle(playerid, 270.0);
	SetPlayerCameraPos(playerid,256.0815,-43.0475,1004.0234);
	SetPlayerCameraLookAt(playerid,258.4893,-41.4008,1002.0234);
}

public OnPlayerRequestClass(playerid, classid)
{
	SetupPlayerForClassSelection(playerid);
	return 1;
}

public OnGameModeInit()
{
	SetGameModeText("Bare Script");
	ShowPlayerMarkers(1);
	ShowNameTags(1);
	AllowAdminTeleport(1);

	AddPlayerClass(265,1958.3783,1343.1572,15.3746,270.1425,0,0,0,0,-1,-1);

	return 1;
}

strtok(const string[], &index)
{
	new length = strlen(string);
	while ((index < length) && (string[index] <= ' '))
	{
		index++;
	}

	new offset = index;
	new result[20];
	while ((index < length) && (string[index] > ' ') && ((index - offset) < (sizeof(result) - 1)))
	{
		result[index - offset] = string[index];
		index++;
	}
	result[index - offset] = EOS;
	return result;
}
The "/crash" command crashes the server.
Код:
echo Executing Server Config...
lanmode 0
rcon_password changemex
maxplayers 500
port 7777
hostname SA-MP 0.3 Server
gamemode0 bare 1
filterscripts
announce 0
query 1
weburl www.sa-mp.com
onfoot_rate 40
incar_rate 40
weapon_rate 40
stream_distance 300.0
stream_rate 1000
maxnpc 10
logtimeformat [%H:%M:%S]
Reply
#2

Why would you actually use '%s' in the SendClientMessage function? Also, as far as I know, '%' is changed to '#'. Though I never tried it as '%s'. I ever wanted a % IG, (I wanted it like this: "Completion: 50%" but this came up: "50#").
Once again, why would you want to use '%s'?
Reply
#3

Quote:
Originally Posted by Kwarde
Посмотреть сообщение
Why would you actually use '%s' in the SendClientMessage function? Also, as far as I know, '%' is changed to '#'. Though I never tried it as '%s'. I ever wanted a % IG, (I wanted it like this: "Completion: 50%" but this came up: "50#").
Once again, why would you want to use '%s'?
To put '%' sign in chat, use '%%'. Won't work by sending in chat, only SendClientMessage.
Reply
#4

Why using %s in SendClientMessage? By the way you can't use any format after the string parameter. Also use HEX as color. I'm not sure, if "0" is pickable as color.

Edit: For using extra formats in SendClientMessage you use "format" function separatly then you gonna replace your "%s" with your output string.
Reply
#5

I know I can use format. I just thought it is worth mentioning.
Reply
#6

Yea, he could fix this, but it still useless. Who is going to put %s in a string + in a function without format support.
Reply
#7

I did not make this. It was provided to me by Emmet_.

SendClientMessageEx(playerid, -1, "%s is a derp.",pName(playerid));

pawn Код:
stock SendClientMessageEx(playerid, color, const string[], { Float, _ }: ...)
{
    new
        tempString[128],
        parts[64],
        tempLen,
        totalFound,
        i,
        j,
        t
    ;
    memcpy(tempString, string, _, (strlen(string) + 1) * 4);

    for(i = 0; tempString[i] != 0; ++i)
    {
        for(j = 0; parts[j] != 0; ++j)
        {
            parts[j] = '\0';
        }
        switch(tempString[i])
        {
            case 's', 'S', 'i', 'I', 'd', 'D', 'f', 'F', 'c', 'C', 'x', 'X', 'b', 'B':
            {
                if(tempString[(i > 0) ? (i - 1) : (i)] != '%')
                {
                    continue;
                }
                else if(tempString[(i > 0) ? (i - 1) : (i)] == '%')
                {
                    ++totalFound;

                    if(tempString[i] == 's' || tempString[i] == 'S')
                    {
                        for(j = 0; getarg(totalFound + 2, j) != 0; ++j)
                        {
                            parts[j] = getarg(totalFound + 2, j);
                        }
                    }
                    switch(tempString[i])
                    {
                        case 'd', 'D', 'i', 'I': format(parts, sizeof(parts), "%d", getarg(totalFound + 2));
                        case 'f', 'F': format(parts, sizeof(parts), "%.02f", getarg(totalFound + 2));
                        case 'c', 'C': format(parts, sizeof(parts), "%c", getarg(totalFound + 2));
                        case 'x', 'X': format(parts, sizeof(parts), "%x", getarg(totalFound + 2));
                        case 'b', 'B': format(parts, sizeof(parts), "%b", getarg(totalFound + 2));
                    }
                    tempLen = strlen(parts);

                    if(tempLen > 2)
                    {
                        for(j = 0; j != (tempLen - 2); ++j)
                        {
                            strins(tempString, "_", (i + 1));
                        }
                        for(j = 0, t = 0; tempString[j] != 0; ++j)
                        {
                            if(j >= (i - 1) && t < tempLen)
                            {
                                tempString[j] = parts[t];
                                ++t;
                            }
                        }
                        continue;
                    }
                    else if(tempLen == 2)
                    {
                        for(j = 0, t = 0; tempString[j] != 0; ++j)
                        {
                            if(j == i || j == (i - 1))
                            {
                                tempString[j] = parts[t];
                                ++t;
                            }
                        }
                        continue;
                    }
                    else if(tempLen < 2)
                    {
                        strdel(tempString, i, i+1);
                        for(j = 0, t = 0; tempString[j] != 0; ++j)
                        {
                            if(j == (i - 1))
                            {
                                tempString[j] = parts[t];
                                ++t;
                            }
                        }
                        continue;
                    }
                }
            }
        }
    }
    if(playerid != INVALID_PLAYER_ID)
        SendClientMessage(playerid, color, tempString);
    else
        SendClientMessageToAll(color, tempString);
    return 1;
}
Reply
#8

if u go ingame and login as /rcon u can send %s %x strings in say/echo and get some weird results too o.O
Reply
#9

Use this instead:
pawn Код:
new string[128]; //adjust this if you want
new name[MAX_PLAYER_NAME];
GetPlayerName(playerid,name,sizeof(name));
format(string, sizeof(string), "This is for an example. By %s", name);
SendClientMessage(playerid, 0xFFFFFF, string);
Just an example of how you could deliver a message with different placeholders
Reply
#10

*He isn't asking for help*
He is reporting a bug! I don't think it's that important to fix this 'bug', 'cause no one in their right minds would send a %s through SCM. (Not offending you, Dan..)
Reply
#11

What will happen if someone do this:
pawn Код:
OnPlayerText(playerid, text[])
{
    new string[128] = ": ", name[MAX_PLAYER_NAME];
    strcat(string, text);
    for(new i = 0, k = strlen(string) - 2;i < k;i++)// Allows players to type % in chat
        if ((string[i] == '(') && (string[i + 1] == '#') && (string[i + 2] == ')'))
        {
            string[i] = '%';
            strdel(string, i + 1, i + 2);
        }
    GetPlayerName(playerid, name, sizeof(name));
    strins(string, name, 0);
    SendClientMessageToAll(playerid, 0xFFFFFFFF, string);// With custom player name, so SendPlayerMessageToAll isn't used.
}
Someone types:
Код:
(#)s
Or similar code in dialogs.
If someone do that, it may cause the problem. Using format will not fix the problem, because it still need SendClientMessageToAll to send the message, which is where the problem at.
By the way, PAWN don't have data type, but only tag. Thus 0 = 0x00000000 = black.
Reply
#12

@leong124:
Still, this does not fix the bug the topic creator reported. And does this work? Because it's still being sent with a client message. (Or I should ask, did you test this? I'm curious right now. Otherwise I'll check this out when I'm back home (I'm at school right now)).
Reply
#13

I'm 99.9% sure this isn't a "bug". Its how its intended, if you need to send formatted messages...use format first.

Just because someone is doing something incorrectly, and it leads to bad results...doesn't mean its a bug. It means something is being done incorrectly.
Reply
#14

Quote:
Originally Posted by Kwarde
Посмотреть сообщение
@leong124:
Still, this does not fix the bug the topic creator reported. And does this work? Because it's still being sent with a client message. (Or I should ask, did you test this? I'm curious right now. Otherwise I'll check this out when I'm back home (I'm at school right now)).
I'm not fixing the bug/problem, but I just want to show you how can this bug occurs in actual situations. The code allows players to type % again by typing (#), but not converting to # by typing %. If someone type (#)s, it will be translated into %s and passed to SendClientMessage(ToAll) and crash the server. I just want to prove that it may not be scripters' fault to make %s appear in SendClientMessage(ToAll).

kaisersouse, is it possible for it to be used as a vulnerability to crash the server if my code is implemented? I've thoughts for adding such code some time ago, and now I am more concerned about the problem.
Reply
#15

It's not a bug, It's a bad code. Just like you can't fill a diesel engine with gasoline, It will break and It's not a bug, It was never intended to run on gasoline.

Just fix the bad code as you will never have any use for It, even If it didn't crash the server It wouldn't do anything.
Reply
#16

'%' is replaced with '#' due to crashing results(formatting). You can only send % sign with SendClientMessage(ToAll) just use %% instead of % .
Reply
#17

Quote:
Originally Posted by Skyrise
Посмотреть сообщение
I did not make this. It was provided to me by Emmet_.

SendClientMessageEx(playerid, -1, "%s is a derp.",pName(playerid));

pawn Код:
stock SendClientMessageEx(playerid, color, const string[], { Float, _ }: ...)
[...]
A friend of mine made this:
Код:
new FALSE = false;
#define SendClientFormattedMessage(%0,%1,%2) do{new _str[128]; format(_str,128,%2); SendClientMessage(%0,%1,_str);}while(FALSE)
#define SendClientFormattedMessageToAll(%1,%2,%3) do{new sendfstring[128];format(sendfstring,128,(%2),%3);SendClientMessageToAll((%1),sendfstring);}while(FALSE)
Which is much shorter and quicker. I think it can still be optimized, though. There's a loop in it, to make sure the "new _str;" doesn't give errors when using this function multiple times in a row. But it might be replaceable with "if (true)" or something similar, or just brackets.
Reply
#18

Quote:
Originally Posted by Basssiiie
Посмотреть сообщение
A friend of mine made this:
Код:
new FALSE = false;
#define SendClientFormattedMessage(%0,%1,%2) do{new _str[128]; format(_str,128,%2); SendClientMessage(%0,%1,_str);}while(FALSE)
#define SendClientFormattedMessageToAll(%1,%2,%3) do{new sendfstring[128];format(sendfstring,128,(%2),%3);SendClientMessageToAll((%1),sendfstring);}while(FALSE)
Which is much shorter and quicker. I think it can still be optimized, though. There's a loop in it, to make sure the "new _str;" doesn't give errors when using this function multiple times in a row. But it might be replaceable with "if (true)" or something similar, or just brackets.
pawn Код:
stock SendFormatMessage(const iPlayer, const iColor, const szFormat[], { Float, _ }: ...) { // Improved by SiX_MiX [Snir]
    new iArgs = ((numargs() - 3) << 2);
    if(iArgs){
        static _:s_szBuf[144],s_iAddr1,s_iAddr2;
        #emit ADDR.PRI szFormat
        #emit STOR.PRI s_iAddr1
        for(s_iAddr2 = s_iAddr1 + iArgs, iArgs += 12; s_iAddr2 != s_iAddr1; s_iAddr2 -= 4) {
            #emit LOAD.PRI s_iAddr2
            #emit LOAD.I
            #emit PUSH.PRI
        }
        #emit CONST.PRI s_szBuf
        #emit PUSH.S szFormat
        #emit PUSH.C 144
        #emit PUSH.PRI
        #emit PUSH.S iArgs
        #emit SYSREQ.C format
        #emit LCTRL 4
        #emit LOAD.S.ALT iArgs
        #emit ADD.C 4
        #emit ADD
        #emit SCTRL 4
        return (iPlayer != -1) ? SendClientMessage(iPlayer, iColor, s_szBuf) : SendClientMessageToAll(iColor, s_szBuf);
    }
    return (iPlayer != -1) ? SendClientMessage(iPlayer, iColor, szFormat) : SendClientMessageToAll(iColor, szFormat);
}
... this is not mine though.
Reply
#19

Quote:
Originally Posted by RootKiller
Посмотреть сообщение
'%' is replaced with '#' due to crashing results(formatting). You can only send % sign with SendClientMessage(ToAll) just use %% instead of % .
Yes you are right, I just have a wrong code.
My code should replace (#) with %% instead of only %.
Also, note that if you want to use % in formatted text for SendClientMessage(ToAll), you will have to use 4 %s, 2 "%%" will be changed into 1 "%%" in format, and changed to "%" in SendClientMessage(ToAll).
Reply
#20

Quote:
Originally Posted by leong124
Посмотреть сообщение
Also, note that if you want to use % in formatted text for SendClientMessage(ToAll), you will have to use 4 %s, 2 "%%" will be changed into 1 "%%" in format, and changed to "%" in SendClientMessage(ToAll).
Are you sure? I haven't tried it, but with files it doesn't work like that. I had %% once in a string in format, I used strins after that and then I wrote the line into a file. Despite the format and strins functions, it still wrote "%%" into the file.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)