Information request buffer allocation

19 views
Skip to first unread message

Mark Rotteveel

unread,
Apr 25, 2025, 4:46:41 AMApr 25
to firebir...@googlegroups.com
Isn't the buffer allocation done for information requests in void
rem_port::info(P_OP op, P_INFO* stuff, PACKET* sendL) (server.cpp) a bit
too naive?

A client can request a buffer of up to 0xFFFE_FFFF (4,294,901,759)
bytes, and the server will happily allocate that, even if the actual
response will be *much* smaller.

Shouldn't this be allocated more incrementally up to the requested length?

Mark
--
Mark Rotteveel

Alex Peshkoff

unread,
Apr 25, 2025, 5:21:17 AMApr 25
to firebir...@googlegroups.com
Mark, no. Correct answer here is 'quotas'. One of them can be max size of information buffer. Without quotas we may again&again find places where client can violate normal server operation.

For example
    insert into some_table select * from endless_stored_procedure;
Disk space is definitely not endless, and I'm afraid it's even more dangerous than too big info buffer :(

PS. Not sure for windows - on linux no physical memory will be given to such buffer as long as no data is written to it. I.e. if actual response is small only single page of RAM is used.


Dimitry Sibiryakov

unread,
Apr 25, 2025, 5:23:40 AMApr 25
to firebir...@googlegroups.com
Alex Peshkoff wrote 25.04.2025 11:21:
> Mark, no. Correct answer here is 'quotas'. One of them can be max size of
> information buffer.

Incremental retrieval of info from the engine is simpler to implement in server.

--
WBR, SD.

Mark Rotteveel

unread,
Apr 25, 2025, 5:35:06 AMApr 25
to firebir...@googlegroups.com
On 25/04/2025 11:21, Alex Peshkoff wrote:
> On 4/25/25 11:46, 'Mark Rotteveel' via firebird-devel wrote:
>> Isn't the buffer allocation done for information requests in void
>> rem_port::info(P_OP op, P_INFO* stuff, PACKET* sendL) (server.cpp) a
>> bit too naive?
>>
>> A client can request a buffer of up to 0xFFFE_FFFF (4,294,901,759)
>> bytes, and the server will happily allocate that, even if the actual
>> response will be *much* smaller.
>>
>> Shouldn't this be allocated more incrementally up to the requested
>> length?
>>
>
> Mark, no. Correct answer here is 'quotas'. One of them can be max size
> of information buffer. Without quotas we may again&again find places
> where client can violate normal server operation.

The point is, if I release a Jaybird version that requests information
items with 0xFFFE_FFFF, that version will happily seem to work (against
64-bit servers only), but it will be slower (the Jaybird tests took 3
minutes longer), and could randomly produce unable to allocate memory
errors on connections (which may or may not be connections of that
Jaybird version).

This would effectively be a form of DDoS, even though it doesn't crash
the server or cause any memory corruption.

The server is too trusting that the client asks for a reasonable size.

>
> For example
>     insert into some_table select * from endless_stored_procedure;
> Disk space is definitely not endless, and I'm afraid it's even more
> dangerous than too big info buffer :(
>
> PS. Not sure for windows - on linux no physical memory will be given to
> such buffer as long as no data is written to it. I.e. if actual response
> is small only single page of RAM is used.

I don't think that is actually true, as the buffer allocation will write
0x00 on the last byte of the buffer (though I'm not sure if that will
cause all memory to be allocated or not). But besides that, have it
occur often enough concurrently will make you run out of virtual memory
space (which will happen in 32-bit Firebird, even if you request
something more reasonable but still overly large).

Mark
--
Mark Rotteveel

Vlad Khorsun

unread,
Apr 25, 2025, 5:39:50 AMApr 25
to firebird-devel
PS. Not sure for windows - on linux no physical memory will be given to such buffer as long as no data is written to it. I.e. if actual response is small only single page of RAM is used.

  The whole buffer is zeroed immediately after allocation:

// Make sure there is a suitable temporary blob buffer
Array<UCHAR> buf;
UCHAR* const buffer = buffer_length ? buf.getBuffer(buffer_length) : NULL;
memset(buffer, 0, buffer_length);

Regards,
Vlad

Dimitry Sibiryakov

unread,
Apr 25, 2025, 5:45:30 AMApr 25
to firebir...@googlegroups.com
'Mark Rotteveel' via firebird-devel wrote 25.04.2025 11:34:
> The server is too trusting that the client asks for a reasonable size.

It is usual for code written in previous millennium.

--
WBR, SD.

Alex Peshkoff

unread,
Apr 25, 2025, 6:01:55 AMApr 25
to firebir...@googlegroups.com
On 4/25/25 12:34, 'Mark Rotteveel' via firebird-devel wrote:
On 25/04/2025 11:21, Alex Peshkoff wrote:
On 4/25/25 11:46, 'Mark Rotteveel' via firebird-devel wrote:
Isn't the buffer allocation done for information requests in void rem_port::info(P_OP op, P_INFO* stuff, PACKET* sendL) (server.cpp) a bit too naive?

A client can request a buffer of up to 0xFFFE_FFFF (4,294,901,759) bytes, and the server will happily allocate that, even if the actual response will be *much* smaller.

Shouldn't this be allocated more incrementally up to the requested length?


Mark, no. Correct answer here is 'quotas'. One of them can be max size of information buffer. Without quotas we may again&again find places where client can violate normal server operation.

The point is, if I release a Jaybird version that requests information items with 0xFFFE_FFFF, that version will happily seem to work (against 64-bit servers only), but it will be slower (the Jaybird tests took 3 minutes longer),

Do you allocate 4Gb of RAM in JayBird for buffer (if yes - is that RAM initialized by JayBird?) or just pass very big size for actually small buffer?
3 minutes - with how long tests?
How will affect tests runtime if in parallel suggested sample with table & procedure runs?


and could randomly produce unable to allocate memory errors on connections (which may or may not be connections of that Jaybird version).

This would effectively be a form of DDoS, even though it doesn't crash the server or cause any memory corruption.

The server is too trusting that the client asks for a reasonable size.


As I've already said - it's too trusting in VERY many cases.



For example
     insert into some_table select * from endless_stored_procedure;
Disk space is definitely not endless, and I'm afraid it's even more dangerous than too big info buffer :(

PS. Not sure for windows - on linux no physical memory will be given to such buffer as long as no data is written to it. I.e. if actual response is small only single page of RAM is used.

I don't think that is actually true, as the buffer allocation will write 0x00 on the last byte of the buffer (though I'm not sure if that will cause all memory to be allocated or not).

On linux - definitely only one page. Suppose similiar behavior on windows but can't say for sure.


But besides that, have it occur often enough concurrently will make you run out of virtual memory space (which will happen in 32-bit Firebird, even if you request something more reasonable but still overly large).


The only known to me place where 32-bit engine is used is embedded access from old Delphi applications. Or may be someone installed not understanding own choice. And yes - I do not think we should care too much about 32 bit systems.


Alex Peshkoff

unread,
Apr 25, 2025, 6:10:20 AMApr 25
to firebir...@googlegroups.com
Hops - now I know where 3 minutes came from.
Suppose this line is worth being removed? Clients do not initialize buffer for getInfo() and everything works fine.


Mark Rotteveel

unread,
Apr 25, 2025, 6:25:20 AMApr 25
to firebir...@googlegroups.com
On 25/04/2025 12:01, Alex Peshkoff wrote:
> On 4/25/25 12:34, 'Mark Rotteveel' via firebird-devel wrote:
>> The point is, if I release a Jaybird version that requests information
>> items with 0xFFFE_FFFF, that version will happily seem to work
>> (against 64-bit servers only), but it will be slower (the Jaybird
>> tests took 3 minutes longer),
>
> Do you allocate 4Gb of RAM in JayBird for buffer (if yes - is that RAM
> initialized by JayBird?) or just pass very big size for actually small
> buffer?

No, Jaybird reads the `p_resp_data`, which is sized to the actual data.
This is purely the server allocating 4 GiB+ as asked by the client
instead of (for example) the 60 bytes the actual response was.

(Pet peeve: it's Jaybird, not JayBird.)

> 3 minutes - with how long tests?

Normal tests run in about 3 minutes, and a lot of those tests do not
even touch the server.

> How will affect tests runtime if in parallel suggested sample with table
> & procedure runs?

I'm not sure what you mean with that.

>> The server is too trusting that the client asks for a reasonable size.
>>
>
> As I've already said - it's too trusting in VERY many cases.

That is no reason to not want to fix that, even if you fix/improve only
one case and not all cases.

>>> PS. Not sure for windows - on linux no physical memory will be given
>>> to such buffer as long as no data is written to it. I.e. if actual
>>> response is small only single page of RAM is used.
>>
>> I don't think that is actually true, as the buffer allocation will
>> write 0x00 on the last byte of the buffer (though I'm not sure if that
>> will cause all memory to be allocated or not).
>
> On linux - definitely only one page. Suppose similiar behavior on
> windows but can't say for sure.

As Vlad already commented, the server memsets the entire buffer to zero,
so it will touch everything.

>> But besides that, have it occur often enough concurrently will make
>> you run out of virtual memory space (which will happen in 32-bit
>> Firebird, even if you request something more reasonable but still
>> overly large).
>>
>
> The only known to me place where 32-bit engine is used is embedded
> access from old Delphi applications. Or may be someone installed not
> understanding own choice. And yes - I do not think we should care too
> much about 32 bit systems.

We release a 32-bit Firebird server, and it gets downloaded quite a lot.

For example, for Windows:

- Firebird-5.0.2.1613-0-windows-x86.exe (the installer, so likely not
used for embedded) - 8132 (GitHub)
- Firebird-5.0.2.1613-0-windows-x86.zip (zip, so possibly server,
possibly embedded) - 4772 (GitHub)

For comparison, 64-bit Windows 5.0.2 is 23276 (installer) and 6028 (zip).

But that was not my point, it was illustrative of that this way the
client can fuck with the server.

Mark
--
Mark Rotteveel

Mark Rotteveel

unread,
Apr 25, 2025, 6:32:37 AMApr 25
to firebir...@googlegroups.com
That addresses only one symptom, and not the whole problem. The client
will still be able to cause the server to allocate far more virtual
address space than needed.

Mark
--
Mark Rotteveel

Alex Peshkoff

unread,
Apr 25, 2025, 6:34:43 AMApr 25
to firebir...@googlegroups.com
On 4/25/25 13:25, 'Mark Rotteveel' via firebird-devel wrote:
On 25/04/2025 12:01, Alex Peshkoff wrote:
On 4/25/25 12:34, 'Mark Rotteveel' via firebird-devel wrote:
The point is, if I release a Jaybird version that requests information items with 0xFFFE_FFFF, that version will happily seem to work (against 64-bit servers only), but it will be slower (the Jaybird tests took 3 minutes longer),

Do you allocate 4Gb of RAM in JayBird for buffer (if yes - is that RAM initialized by JayBird?) or just pass very big size for actually small buffer?

No, Jaybird reads the `p_resp_data`, which is sized to the actual data. This is purely the server allocating 4 GiB+ as asked by the client instead of (for example) the 60 bytes the actual response was.

(Pet peeve: it's Jaybird, not JayBird.)

3 minutes - with how long tests?

Normal tests run in about 3 minutes, and a lot of those tests do not even touch the server.

How will affect tests runtime if in parallel suggested sample with table & procedure runs?

I'm not sure what you mean with that.


Forget it, Vlad's email explains everything.


The server is too trusting that the client asks for a reasonable size.


As I've already said - it's too trusting in VERY many cases.

That is no reason to not want to fix that, even if you fix/improve only one case and not all cases.


Just don't forget ti mention in comments that it's solution due to missing quotes.
And I do not think there is much use in any cycles here - if info buffer gets larger say 1Mb something appears to be wrong.


PS. Not sure for windows - on linux no physical memory will be given to such buffer as long as no data is written to it. I.e. if actual response is small only single page of RAM is used.

I don't think that is actually true, as the buffer allocation will write 0x00 on the last byte of the buffer (though I'm not sure if that will cause all memory to be allocated or not).

On linux - definitely only one page. Suppose similiar behavior on windows but can't say for sure.

As Vlad already commented, the server memsets the entire buffer to zero, so it will touch everything.


I've trusted too much to our codebase :-(


But besides that, have it occur often enough concurrently will make you run out of virtual memory space (which will happen in 32-bit Firebird, even if you request something more reasonable but still overly large).


The only known to me place where 32-bit engine is used is embedded access from old Delphi applications. Or may be someone installed not understanding own choice. And yes - I do not think we should care too much about 32 bit systems.

We release a 32-bit Firebird server, and it gets downloaded quite a lot.

For example, for Windows:

- Firebird-5.0.2.1613-0-windows-x86.exe (the installer, so likely not used for embedded) - 8132 (GitHub)
- Firebird-5.0.2.1613-0-windows-x86.zip (zip, so possibly server, possibly embedded) - 4772 (GitHub)

For comparison, 64-bit Windows 5.0.2 is 23276 (installer) and 6028 (zip).

Well if you have big desire to fix this particular case... let it be.


Mark Rotteveel

unread,
Apr 25, 2025, 7:12:55 AMApr 25
to firebir...@googlegroups.com
On 25/04/2025 12:34, Alex Peshkoff wrote:
> On 4/25/25 13:25, 'Mark Rotteveel' via firebird-devel wrote:
>> On 25/04/2025 12:01, Alex Peshkoff wrote:
>>> On 4/25/25 12:34, 'Mark Rotteveel' via firebird-devel wrote:
>>>> The server is too trusting that the client asks for a reasonable size.
>>>
>>> As I've already said - it's too trusting in VERY many cases.
>>
>> That is no reason to not want to fix that, even if you fix/improve
>> only one case and not all cases.
>
> Just don't forget ti mention in comments that it's solution due to
> missing quotes.

? What do you mean with this ?

> And I do not think there is much use in any cycles here - if info buffer
> gets larger say 1Mb something appears to be wrong.

That is a naive assumption. An op_info_sql asking for among others,
isc_info_sql_field, isc_info_sql_alias, isc_info_sql_relation,
isc_info_sql_relation_alias, isc_info_sql_owner, for a statement with a
lot of output columns can be pretty wide.

In theory, if all those identifiers are 63 characters, and those
characters all require 4 bytes to be encoded, the response can be 5 *
252 = 1260 bytes *per column* (ignoring all other info items asked for
and ignoring the overhead of the encoding of these items in the buffer),
so selecting something with 832 columns like that would already have an
info response of more than 1 MiB.

And, yes, I know that it is an absurd example, but it's intended to
illustrates that your thinking about potential sizes of things is a bit
off IMHO.

But I think we've all seen people using a database as a glorified Excel
sheet, so say a table has only INTEGER columns. IIRC, current
limitations are 32KiB for a row, so you can fit 8192 INTEGER columns in
a single query column list (I guess there might be some additional
overhead I'm ignoring).

If we take your suggested 1 MiB as an upper limit, and only consider
those info items (and not even the overhead of their encoding), that
means you can have 128 bytes per column, say 25 bytes per item. That is
not a lot if you consider that they have a maximum length of 252 bytes
*each*.

Again, I know it's an absurd example (but more likely to occur in the
wild than my earlier example).

You should not only take the likely cases into account, but also the
extremes.

Mark
--
Mark Rotteveel

Mark Rotteveel

unread,
Apr 25, 2025, 7:15:43 AMApr 25
to firebir...@googlegroups.com
On 25/04/2025 12:34, Alex Peshkoff wrote:
> Well if you have big desire to fix this particular case... let it be.

I think my C++ knowledge is not sufficient to try and tackle that.

Mark
--
Mark Rotteveel

Mark Rotteveel

unread,
Apr 25, 2025, 7:34:12 AMApr 25
to firebir...@googlegroups.com
On 25/04/2025 11:34, 'Mark Rotteveel' via firebird-devel wrote:
> The point is, if I release a Jaybird version that requests information
> items with 0xFFFE_FFFF, that version will happily seem to work (against
> 64-bit servers only), but it will be slower (the Jaybird tests took 3
> minutes longer), and could randomly produce unable to allocate memory
> errors on connections (which may or may not be connections of that
> Jaybird version).
>
> This would effectively be a form of DDoS, even though it doesn't crash
> the server or cause any memory corruption.

Small correction: the Jaybird tests took 3 minutes longer when I only
modified its op_info_sql requests.

If I modify *all* info requests (at least the ones that take the same
path in Jaybird), the tests take 46 minutes longer (from 3 minutes to 49
minutes).

Mark
--
Mark Rotteveel

Dimitry Sibiryakov

unread,
Apr 25, 2025, 9:51:34 AMApr 25
to firebir...@googlegroups.com
'Mark Rotteveel' via firebird-devel wrote 25.04.2025 12:32:
> That addresses only one symptom, and not the whole problem. The client will
> still be able to cause the server to allocate far more virtual address space
> than needed.

In the past we already discussed a new API call getInfo2() that doesn't
accept client buffer but returns XBuilder with internally allocated info data
buffer.
Perhaps it is time to think about it once again.

--
WBR, SD.

Alex Peshkoff

unread,
Apr 25, 2025, 11:40:52 AMApr 25
to firebir...@googlegroups.com
Sounds good for me. Plain buffer in getInfo() is definitely guest from XX century. We will keep getInfo() for backward compatibility but nothing forces server to use it instead getInfo2().


Mark Rotteveel

unread,
Apr 25, 2025, 1:28:47 PMApr 25
to firebir...@googlegroups.com
That sounds like equating the native client API with the wire protocol,
which is what caused this problem in the first place.

Mark
--
Mark Rotteveel

Vlad Khorsun

unread,
Apr 25, 2025, 2:03:14 PMApr 25
to firebir...@googlegroups.com
25.04.2025 13:10, Alex Peshkoff:
I tend to agree, it looks not necessary.

Regards,
Vlad

Dimitry Sibiryakov

unread,
Apr 25, 2025, 3:30:27 PMApr 25
to firebir...@googlegroups.com
'Mark Rotteveel' via firebird-devel wrote 25.04.2025 19:28:
>>    In the past we already discussed a new API call getInfo2() that doesn't
>> accept client buffer but returns XBuilder with internally allocated info data
>> buffer.
>>    Perhaps it is time to think about it once again.
>
> That sounds like equating the native client API with the wire protocol, which is
> what caused this problem in the first place.

From the beginning Jim designed client library as a simple remote procedure
call proxy, so yes, network protocol used to mimic native API.

--
WBR, SD.

Alex Peshkoff

unread,
Apr 27, 2025, 11:40:05 AMApr 27
to firebir...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages