OpenSIPS version you are running
Latest master (commit a74fc46, 2026-03-19)
Describe the bug
recv_smpp_msg() in modules/proto_smpp/smpp.c uses a fixed-size static buffer sms_body[280] (i.e. 2 * MAX_SMS_CHARACTERS) to hold the converted message body. However, the sm_length field from the incoming SMPP PDU (uint8_t, range 0–255) is passed directly to convert_gsm7_to_utf8() and string2hex() without any bounds checking.
Both conversion functions can produce output significantly larger than 280 bytes, causing a static buffer overflow write of up to 230 bytes.
Path 1: GSM7 default encoding (data_coding != UCS2)
// smpp.c, recv_smpp_msg()
static char sms_body[2*MAX_SMS_CHARACTERS]; // 280 bytes
body_str.len = convert_gsm7_to_utf8((unsigned char *)body->short_message,
body->sm_length, sms_body);
Many GSM7 characters (e.g. index 0x01 = £, 0x03 = ¥, 0x04 = è) map to Unicode codepoints > 0x7F, requiring 2 bytes of UTF-8 output per input byte. With sm_length = 255: output up to 510 bytes into a 280-byte buffer → overflow 230 bytes.
Path 2: UCS-2 encoding (data_coding == 8)
body_str.len = string2hex((char *)body->short_message,
body->sm_length, sms_body);
string2hex() produces exactly 2 output bytes per input byte. With sm_length = 255: output 510 bytes → same 230-byte overflow.
Root cause: sm_length is read from the PDU without validation:
body->sm_length = *p++;
copy_fixed_str(body->short_message, p, body->sm_length);
No check that sm_length <= MAX_SMS_CHARACTERS before the conversion functions.
Note: short_message is char[254], so sm_length = 255 also causes a 1-byte overflow in copy_fixed_str.
To Reproduce
Compile the following standalone PoC with AddressSanitizer:
// Compile: clang -fsanitize=address -g -o poc_gsm7 poc_gsm7.c
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define MAX_SMS_CHARACTERS 140
static unsigned int table_gsm7_to_utf8[] = {
'@', 0xA3, '$', 0xA5, 0xE8, 0xE9, 0xF9, 0xEC,
0xF2, 0xC7, 0x10, 0xd8, 0xF8, 0x13, 0xC5, 0xE5,
0x394, '_', 0x3A6, 0x393, 0x39B, 0x3A9, 0x3A0, 0x3A8,
0x3A3, 0x398, 0x39E, '?', 0xC6, 0xE6, 0xDF, 0xC9,
' ', '!', '"', '#', 0xA4, '%', '&', '\'',
'(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', ':', ';', '<', '=', '>', '?',
0xA1, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', 0xC4, 0xD6, 0xD1, 0xDC, 0xA7,
0xBF, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', 0xE4, 0xF6, 0xF1, 0xFC, 0xE0,
};
static int convert_gsm7_to_utf8(unsigned char *input, int input_len, char *output) {
char *p = output;
int i, t;
unsigned char c;
for (i = 0; i < input_len; i++) {
c = input[i];
if (c == 0x1B) { switch (input[++i]) {
case 0x14: t = '^'; break; case 0x28: t = '{'; break;
case 0x29: t = '}'; break; case 0x2F: t = '\\'; break;
case 0x3C: t = '['; break; case 0x3D: t = '~'; break;
case 0x3E: t = ']'; break; case 0x40: t = '|'; break;
case 0x65: t = 0x20AC; break;
default: --i; case 0x0D: case 0x1B: t = '?'; break;
}} else if (c < 0x80) t = table_gsm7_to_utf8[c]; else t = c;
if (t > 0x7F) {
if (t > 0x800) { *p++ = 0xE0|((t>>12)&0x0F); *p++ = 0x80|((t>>6)&0x3F); *p++ = 0x80|(t&0x3F); }
else { *p++ = 0xC0|((t>>6)&0x1F); *p++ = 0x80|(t&0x3F); }
} else *p++ = (unsigned char)t;
}
return p - output;
}
int main(void) {
static char sms_body[2 * MAX_SMS_CHARACTERS]; // 280 bytes, same as OpenSIPS
unsigned char input[200];
memset(input, 0x01, sizeof(input)); // 0x01 = £ → 2-byte UTF-8 (0xC2 0xA3)
convert_gsm7_to_utf8(input, 200, sms_body); // 400 bytes output → 120 bytes overflow
return 0;
}
Run:
$ clang -fsanitize=address -g -o poc_gsm7 poc_gsm7.c && ./poc_gsm7
==ERROR: AddressSanitizer: global-buffer-overflow on address ...
WRITE of size 1 at ... thread T0
#0 in convert_gsm7_to_utf8
#1 in main
... is located 0 bytes after global variable 'sms_body' of size 280
Expected behavior
sm_length should be validated against MAX_SMS_CHARACTERS before being passed to the conversion functions. PDUs with sm_length > 140 (or > 254 for the short_message array) should be rejected.
Relevant System Logs
Without ASAN, this corrupts adjacent static/global variables silently. With ASAN enabled, the process aborts on the buffer overflow write.
OS/environment information
- Operating System: Ubuntu 22.04
- OpenSIPS installation: git (master, commit a74fc46)
Additional context
- CWE: CWE-787 (Out-of-bounds Write)
- Attack vector: Network — send a crafted
deliver_sm or submit_sm SMPP PDU with sm_length > 140 and GSM7 characters that expand to multi-byte UTF-8 (or UCS-2 encoding path)
- Authentication: The
handle_smpp_msg() dispatch does not check session bind state, so this is reachable without authentication
- Severity: High — static buffer overflow write of up to 230 bytes, corruption of adjacent global state, potential crash or code execution
Suggested fix — validate sm_length before conversion:
if (body->sm_length > MAX_SMS_CHARACTERS) {
LM_ERR("sm_length %d exceeds maximum %d\n", body->sm_length, MAX_SMS_CHARACTERS);
return -1;
}
OpenSIPS version you are running
Describe the bug
recv_smpp_msg()inmodules/proto_smpp/smpp.cuses a fixed-size static buffersms_body[280](i.e.2 * MAX_SMS_CHARACTERS) to hold the converted message body. However, thesm_lengthfield from the incoming SMPP PDU (uint8_t, range 0–255) is passed directly toconvert_gsm7_to_utf8()andstring2hex()without any bounds checking.Both conversion functions can produce output significantly larger than 280 bytes, causing a static buffer overflow write of up to 230 bytes.
Path 1: GSM7 default encoding (
data_coding != UCS2)Many GSM7 characters (e.g. index 0x01 =
£, 0x03 =¥, 0x04 =è) map to Unicode codepoints > 0x7F, requiring 2 bytes of UTF-8 output per input byte. Withsm_length = 255: output up to 510 bytes into a 280-byte buffer → overflow 230 bytes.Path 2: UCS-2 encoding (
data_coding == 8)string2hex()produces exactly 2 output bytes per input byte. Withsm_length = 255: output 510 bytes → same 230-byte overflow.Root cause:
sm_lengthis read from the PDU without validation:No check that
sm_length <= MAX_SMS_CHARACTERSbefore the conversion functions.Note:
short_messageischar[254], sosm_length = 255also causes a 1-byte overflow incopy_fixed_str.To Reproduce
Compile the following standalone PoC with AddressSanitizer:
Run:
Expected behavior
sm_lengthshould be validated againstMAX_SMS_CHARACTERSbefore being passed to the conversion functions. PDUs withsm_length > 140(or> 254for theshort_messagearray) should be rejected.Relevant System Logs
Without ASAN, this corrupts adjacent static/global variables silently. With ASAN enabled, the process aborts on the buffer overflow write.
OS/environment information
Additional context
deliver_smorsubmit_smSMPP PDU withsm_length > 140and GSM7 characters that expand to multi-byte UTF-8 (or UCS-2 encoding path)handle_smpp_msg()dispatch does not check session bind state, so this is reachable without authenticationSuggested fix — validate
sm_lengthbefore conversion: