-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamically resized the buffer for the sdp answer if the buffer is not large enough #2793
Conversation
Why not use realloc instead of a new malloc+memcpy? |
Can also use realloc. Will update it later. Other comments? |
Other comment would be this should probably be a core method, like the one @tmatth sketched so far, so that we can use it in other places where we currently use g_strlcat. For this reason, my idea is to merge that first (once we've reviewed it), and then we can modify the new We should ensure this is never used on static buffers, though: in this case we're using |
for resizing now either using the required length or we double the size
Changed it to realloc and improved it in case the string is larger than the "static buffer length increase" it failed |
I think doubling the size is dangerous, because it means exponentially growing the buffer when something goes wrong. I'd personally just increase the buffer to what's required in that specific call. |
If we just increase by the size we need that leads to super many reallocs. This is not time critical for the SDP answer but if the function is generally used this might have impacts. |
I agree with @lminiero about being SUPER cautious when using this helper with stack allocated buffers. I also agree with @JanFellner about getting the Finally what is missing here is a maximum value. We should not let SDPs grow to undefined sizes. |
I´ll add a maximum, the question is then what to do in case we reach it. |
I'd say no but I understand it's very redundant to check for a truncation ( |
⚠In case the buffer is exceeded, every further cat will create an error but without a reference where it actually failed. I just mentions the required and the maximum buffer size. ❓ The cat is partly executed like now or should we not cat that element at all? Or should we target these two questions with the janus_strcat #2792 and output related errors there? (e.g. first 50 bytes of the target buffer, first 50 bytes of the appendum, otherwise it might be hard to analyze if the method is widely used) |
Just to have it mentioned: |
Systematic core dump on a maximum means leaving a door open to just anyone willingly crashing the system: all they need to do is have Janus craft a huge SDP and they know it will crash. I'll never add something like that, sorry. |
I am fine with anything. Just mentioned my concerns. |
I just merged #2792 and aligned multistream to use it too. Now we can check what the best way to address it is. I think we'll need a different method from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some notes after merging #2792.
@@ -20,6 +20,9 @@ | |||
#include "debug.h" | |||
|
|||
#define JANUS_BUFSIZE 8192 | |||
#define MAX_JANUS_BUFSIZE 1048576 | |||
|
|||
gsize dynamic_strlcat(char** buffer, const char* add, gsize size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This belongs to utils.c
(and the related signature in utils.h
) as otherwise no other part of the code except the SDP utils will be able to use this (even though this is the only place we'll use this initially).
I'd also rename it to something like janus_strlcat_dynamic
or janus_strlcat_realloc
, to make it more consistent with the existing janus_strlcat
and make the difference in functionality more obvious.
/*! \brief Concatenates a string to another and dynamically increases the buffer if it is too small | ||
* @param dynamic_buffer - Pointer to the dynamic buffer for the target string. This must be a dynamic allocated memory. | ||
* @param add - String to add | ||
* @param size - Current size of the buffer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just as a note, the -
(dash) is not needed, doxygen will figure out what's the variable and what's the description.
That said, this description will only need to be added to utils.h
, where the signature will be.
* @param size - Current size of the buffer | ||
* @returns the size of the buffer (may exceed size if it was resized) | ||
*/ | ||
gsize dynamic_strlcat(char** dynamic_buffer, const char* add, gsize size) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use the same names for the variables in the signature of janus_strlcat
, for consistency.
On a side note, shouldn't the last argument be a pointer, i.e., gsize *size
? This would allow the method to update the current size of the dynamic buffer dynamically using an input/output variable. I prefer this to re-assigning the variable from the return value as you do now, as in case we reach the maximum size, the returned value will be higher (which is what Tristan used as a way to detect failures).
In that case, we can change the return value to something different, e.g., an integer where 0
means success and a negative value an error, or a boolean: this way, we can detect failures from the calling side, and react consequently.
* @returns the size of the buffer (may exceed size if it was resized) | ||
*/ | ||
gsize dynamic_strlcat(char** dynamic_buffer, const char* add, gsize size) { | ||
// What is the resulting length (current used buffer size + length of the addition + null byte) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code style: comments should all be in /* */
form.
// In case the buffer is not large enough | ||
if(required > size) { | ||
if(required > MAX_JANUS_BUFSIZE) | ||
JANUS_LOG(LOG_ERR, "dynamic_strlcat - Required buffer size %ld exceeds MAX_JANUS_BUFSIZE (%d)", required, MAX_JANUS_BUFSIZE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be a return here as well: no point in going further.
if(required > MAX_JANUS_BUFSIZE) | ||
JANUS_LOG(LOG_ERR, "dynamic_strlcat - Required buffer size %ld exceeds MAX_JANUS_BUFSIZE (%d)", required, MAX_JANUS_BUFSIZE); | ||
// Either we double the size, or we increase by the required size | ||
size_t newsize = MAX(size * 2, required); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Googling around, it looks like a more reasonable way to increase the buffer is to use a 1.5 grow factor, rather than two. Doubling the buffer does make me a bit nervous 😄
// Either we double the size, or we increase by the required size | ||
size_t newsize = MAX(size * 2, required); | ||
if(newsize > MAX_JANUS_BUFSIZE) | ||
newsize = MAX_JANUS_BUFSIZE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this check isn't needed anymore, if we stop before that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The max is checking whether doubling (or 1.5ing) is bigger than required.
So the resulting newsize may actually be bigger than the maximum allowed buffer size in case:
where the size*1.5 is larger but the required is just less than the maximum buffer size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, thanks for the clarification!
*dynamic_buffer = g_realloc(*dynamic_buffer, size); | ||
} | ||
} | ||
g_strlcat(*dynamic_buffer, add, size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd still check what the return value of this function is, just in case there can be other causes of issues. Maybe we can do something like this instead:
return janus_strlcat(*dynamic_buffer, add, size);
since that method has the necessary check in place, and so we can delegate it to that.
temp2 = temp2->next; | ||
} | ||
temp = temp->next; | ||
} | ||
janus_refcount_decrease(&imported->ref); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This extra empty line is not needed.
PS: please let me know if you'd like me to address this transition instead (especially considering there's conflicts to address too). |
Yeah Lorenzo, for the complexity of the function and the amount of communication we would both need just go ahead :). I go for all your proposed changes, the variable naming was my failure as i tried to rename the arguments to better clearify that the buffer needs to be dynamic but forgot about the header there. ❗ Just check the only comment about the compare against the max buffer size #2793 (comment) |
And then burry the PR... ⚰ |
I just noticed that this patch was for master and not multistream. In multistream we already use realloc for the SDP, so it's not needed there: I can use the same pattern we use there in master too, which would be simpler and wouldn't require a separate function. |
@tmatth @JanFellner can you check if the above PR fixes it for you? I tried to replicate the issue myself, but I don't have as many interfaces as Jan (assing STUN/TURN gathering helped a bit but not much), so I couldn't grow m-lines to the point they were needed. |
checking... |
Working. Please close it and merge the other one :) |
Ack, thanks! I'll make a couple of other tests later (just to be sure we don't find regressions) and then merge. |
Closing, thanks for notifying about the issue and proposing a fix! |
Follow up from this one:
#2791