Skip to content
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

queue.c: Change some asserts into conditionals and improve overflow checks #328

Merged
merged 3 commits into from
May 27, 2021

Conversation

dan4thewin
Copy link
Contributor

This PR is paired with changes to the CBMC queue proofs: FreeRTOS/FreeRTOS#603

These changes allow certain CBMC proofs to be written with few or no assumptions. The overflow checks now preempt an overflow, rather than checking for the consequence of an overflow.

@dan4thewin dan4thewin requested a review from a team as a code owner May 19, 2021 20:51
Copy link
Member

@AniruddhaKanhere AniruddhaKanhere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see the comments.
I am not sure whether replacing multiple asserts with just one assert would benefit users.

queue.c Outdated Show resolved Hide resolved
queue.c Outdated
@@ -369,8 +375,7 @@ BaseType_t xQueueGenericReset( QueueHandle_t xQueue,
}
else
{
traceQUEUE_CREATE_FAILED( ucQueueType );
mtCOVERAGE_TEST_MARKER();
configASSERT( pdFAIL );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This configASSERT would not help while debugging. In the previous source, one could simply see which configASSERT (with a proper implementation using __FILE__ and __LINE__) was failing and find the issue.
Now one would need to print out the values or step through the code with debugger. Shall we try to find a better way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the approach Richard suggested. Changing the asserts to conditionals means there is still protection when assertions are compiled out, but loses debugging information as you point out. Adding in the trailing assert is a compromise to provide some information without repeating every check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Can we add all the asserts instead of the single assert? That would be a win win. In that case, we'll be able to debug effectively and protection will still be there if the assertions are compiled out.
I am asking this since we are anyways making a compromise with the pdFAIL assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming you mean that we could have an else block where the tests are repeated as asserts. Manually keeping the two sets of tests in sync strikes me as a big drawback. Let's get Richard's opinion.

Copy link
Contributor

@paulbartell paulbartell May 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great place for an ASSERT_CONTINUE macro. The idea would be to log the assertion to NV storage or to a debugging port (uart or similar), but not go into the typical infinite loop. This also lets customers test the edge cases in their applications without entering an infinite loop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as in the queue reset function about using a constant as the parameter in the configASSERT() macro - will generate compiler warnings ("condition is always true"). Extending Paul's thoughts above, what we really need is an assert always function. I think, also as per my comments in the queue reset function, using a status variable that gets set to pass or fail, then asserting on the status variable just before exiting the function would be fine. I understand that loses information when debugging, but if you see that line is failing I think it would be possible to see why from within the debugger.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to assert on the return value

queue.c Outdated Show resolved Hide resolved
queue.c Outdated Show resolved Hide resolved
queue.c Outdated Show resolved Hide resolved
@paulbartell
Copy link
Contributor

Note: the unit tests for queue.c need to be updated over in the FreeRTOS/FreeRTOS repository ( https://github.com/FreeRTOS/FreeRTOS/tree/main/FreeRTOS/Test/CMock/queue ).

To validate that they run correctly on github, I would suggest making a branch in the FreeRTOS/FreeRTOS repository with the Source submodule pointer set to the tip of this branch. Then update the tests as necessary and disregard any failures displayed for unit tests in this PR.

Alternatively, you could modify the github workflow for unit tests in this repository to check for a branch with the same name from the same user's fork and use that branch is available.

markrtuttle
markrtuttle previously approved these changes May 20, 2021
@RichardBarry
Copy link
Contributor

Brief answer for now due to it being late - I need to do a deeper dive into the PR before a full answer.

My understanding is that the prior asserts at the top of the file made the CBMC memory safety checks cumbersome and potentially long running - hence they were replaced with inline checks that are much more CBMC friendly. The new assert at the end then made it backward compatible for code that relied on the assert behavior.

I understand the desire to make it easier to debug - debuggability is a very important attribute. Adding the asserts() back at the front would probably re-introduce the CBMC issue this PR is trying to improve, plus it would look cumbersome to effectively have the same checks twice. It may be possible to replace the single assert at the end with something that provides more information to developers, but as per, I would need to look a bit deeper before having an opinion on that.

if( ( pxQueue != NULL ) &&
( pxQueue->uxLength >= 1 ) &&
/* Check for multiplication overflow. */
( ( SIZE_MAX / pxQueue->uxLength ) >= pxQueue->uxItemSize ) )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uxLength and uxItemSize are both UBaseType_t, rather than size_t, so this assumes sizeof( UBaseType_t ) == sizeof( size_t ) -> can we create a local constant that is equivalent to UBaseType_t_MAX?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think SIZE_MAX or PTRDIFF_MAX are reasonable choices. Given that the pxQueue->uxLength and pxQueue->uxItemSize are used to do pointer arithmetic and also with pvPortMalloc(), we want the product of these to always be <= to the largest theoretical object.

queue.c Outdated
@@ -268,8 +268,13 @@ BaseType_t xQueueGenericReset( QueueHandle_t xQueue,

configASSERT( pxQueue );

taskENTER_CRITICAL();
if( ( pxQueue != NULL ) &&
( pxQueue->uxLength >= 1 ) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should put a "U" on the 1, as per 1U.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

queue.c Outdated
}
else
{
configASSERT( pdFAIL );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with asserting on "pdFAIL" is that the condition is always true so this will generate compiler warnings. That is - the assert test will always trigger (which is the intention of course), and there is no ASSERT_ALWAYS() macro.

The previous version of this function only asserted when pxQueue was NULL, and that assert still exists at the top of the file, so that behaviour has not changed. Now however the code will also assert if either of the two additional checks fail. Perhaps introduce a status variable that is set to pdPASS in the happy path, and pdFAIL on what is currently line 318 - then assert on and return the status variable at the end of the function:

else
{
    xReturn = pdFAIL;
}

configASSERT( xReturn != pdFAIL );
return xReturn;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

queue.c Outdated
@@ -369,8 +375,7 @@ BaseType_t xQueueGenericReset( QueueHandle_t xQueue,
}
else
{
traceQUEUE_CREATE_FAILED( ucQueueType );
mtCOVERAGE_TEST_MARKER();
configASSERT( pdFAIL );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as in the queue reset function about using a constant as the parameter in the configASSERT() macro - will generate compiler warnings ("condition is always true"). Extending Paul's thoughts above, what we really need is an assert always function. I think, also as per my comments in the queue reset function, using a status variable that gets set to pass or fail, then asserting on the status variable just before exiting the function would be fine. I understand that loses information when debugging, but if you see that line is failing I think it would be possible to see why from within the debugger.

queue.c Outdated
}
else
{
traceQUEUE_CREATE_FAILED( ucQueueType );
mtCOVERAGE_TEST_MARKER();
configASSERT( pdFAIL );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here on asserting on an always true test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

@dan4thewin dan4thewin merged commit 8e2f723 into FreeRTOS:main May 27, 2021
laroche pushed a commit to laroche/FreeRTOS-Kernel that referenced this pull request Apr 18, 2024
…#328)

Add missing power libraries. Also increase the size of the privileged
data section as heap is now placed in the privileged data section.

Signed-off-by: Gaurav Aggarwal <aggarg@amazon.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants