buffer_read - but it won't crash if you read beyond the end

GM Version: 1.4
Target Platform: ALL
Download
: N/A
Links: N/A

Summary:
If you try to read beyond the end of a buffer, you will get an error. This is a problem if your game uses buffers for networking, because anyone could maliciously send a buffer that ends unexpectedly, triggering this error. However this script will prevent that from happening, by giving you the option to specify a default value to return instead, allowing you to detect when something is wrong and abort, similar to how ini files specify a default.


Script:
Code:
///buffer_read_ext(buffer,bufferType,default)
/*
Works the same as ini_read_* functions
If a value is not found, it will return the default value specified.

Without the use of this script, anyone can crash your game by sending a packet that's too short
resulting in a "trying to read outside buffer" error.


Returns value read from buffer or specified default
*/

// Arguments
var buffer     = argument0; // The buffer to read from
var type       = argument1; // The buffer type to read data as
var defaultVal = argument2; // What to return if this data is missing

// Work out how many bytes the type uses
var expectedSize = undefined;
switch type
    {
    // Real
    case buffer_u8:  {expectedSize = 1;} break; // Unsigned 8 bits  (1 byte)  0 to 255
    case buffer_u16: {expectedSize = 2;} break; // Unsigned 16 bits (2 bytes) 0 to 65,535
    case buffer_u32: {expectedSize = 4;} break; // Unsigned 32 bits (4 bytes) 0 to 4,294,967,295
    case buffer_u64: {expectedSize = 8;} break; // Unsigned 64 bits (8 bytes) 0 to 18,446,744,073,709,551,615
    case buffer_s8:  {expectedSize = 1;} break; // Signed   8  bits (1 byte)  -128 to 127
    case buffer_s16: {expectedSize = 2;} break; // Signed   16 bits (2 bytes) -32,768 to 32,767
    case buffer_s32: {expectedSize = 4;} break; // Signed   32 bits (4 bytes) -2,147,483,648 to 2,147,483,647
    case buffer_f16: {expectedSize = 2;} break; // Float    16 bits (2 bytes)  supports decimal place
    case buffer_f32: {expectedSize = 4;} break; // Float    32 bits (4 bytes)
    case buffer_f64: {expectedSize = 8;} break; // Float    64 bits (8 bytes)
   
    // String
    case buffer_text:   {expectedSize = 0;} break; // String 0+ bits [string length] bytes
    case buffer_string: {expectedSize = 1;} break; // String 8+ bits ([string length + 1] bytes) (has a terminating byte (0))
   
    // Error
    default: {show_message("ERROR: buffer_* type not supported.");} break;
    }
   
// Get where the buffer read position is
var buffPos  = buffer_tell(buffer);

// If it's actually possible to read this type
if buffer_get_size(buffer) >= buffPos+expectedSize
    {
    // Read it
    return buffer_read(buffer,type);
    }
else // Otherwise, this type would extend beyond the buffer (and cause an error!)
    {
    // So move the buffer position to the end to prevent any further reading
    buffer_seek(buffer,buffer_seek_end,0);
   
    // And return the default
    return defaultVal;
    }
 
Last edited:
Top