Skip to content

Conversation

@dANW34V3R
Copy link
Contributor

Fixes #426 among other errors.

Add a safe pointer struct to remove undefined behaviour caused by reinterpret casting in the RegisterValue. The safe pointer hides the underlying pointer to RegisterValue data. This enforces immutability and fixes UB by converting unsafe reinterpret_casts to safe memcpys.


// Check fstatat returned 0
EXPECT_EQ(getGeneralRegister<int64_t>(27), 0);
EXPECT_EQ(getGeneralRegister<int64_t>(21), 0);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@FinnWilkinson @jj16791 I think this test was checking the wrong register. Can you double check?

Copy link
Contributor

Choose a reason for hiding this comment

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

If we are wanting to check the newfstatat register, then it sould be 21 yeah. My sme branch has register 21 being checked though so unsure when this happened...

Comment on lines +51 to +52
stateChange.memoryAddressValues.push_back(RegisterValue(
reinterpret_cast<const uint8_t*>(out.data()), outSize));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reinterpret_casting throughout this function is alarming and requires further investigation

Copy link
Member

Choose a reason for hiding this comment

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

Personally I think RegisterValue should have proper move semantics and you should be able to create a blank register of some size and move it into the vector


// Write data for this buffer in 128-byte chunks
auto iSrc = reinterpret_cast<const char*>(buffers[i].data());
auto iSrc = reinterpret_cast<const uint8_t*>(buffers[i].data());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure about this one

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comments same as AArch exception handler

Comment on lines +80 to +83
// TODO avoid malloc and copy altogether
uint8_t* data = (uint8_t*)malloc(size);
aggrReq->data.getAsVector<char>().copyTo(data, size);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could have a member buffer but not sure what size?

Comment on lines +5644 to +5651
reinterpret_cast<uint8_t*>(memData.data()), memData.size() * sizeof(uint64_t));
index++;
memData.clear();
}
}
// Check if final data needs putting into memoryData_
if (memData.size() > 0) {
memoryData_[index] = RegisterValue((char*)memData.data(),
memoryData_[index] = RegisterValue(reinterpret_cast<uint8_t*>(memData.data()),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could be dangerous

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reinterpret casts throughout need to be checked for safety

Copy link
Contributor

@ABenC377 ABenC377 left a comment

Choose a reason for hiding this comment

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

Looks good. A couple of general questions (more for my own understanding than anything else).


for (int i = 0; i < I; i++) {
if (isXtn2 & (i < (I / 2))) {
assert(isXtn2 && "isXtn2 is false so d is not initialised");
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this assert ever fail? If isXtn2 & anything is true, wont isXtn2 always be true?

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 is layover from when I was having issues previously. No harm in keeping it but happy to remove. Your call

Copy link
Contributor

Choose a reason for hiding this comment

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

I think get rid of it, as it doesn't do anything and so might cause momentary confusion for someone reading this bit of code in the future.

assert(response->data && "unhandled failed read in exception handler");
uint8_t bytesRead = response->target.size;
const uint8_t* data = response->data.getAsVector<uint8_t>();
uint8_t* data = (uint8_t*)malloc(bytesRead);
Copy link
Member

Choose a reason for hiding this comment

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

Create a block-scoped std::vector<uint8_t> and copy to that instead of malloc/free

memcpy(dest, ptr, bytes);
}

void copyTo(void* dest, const size_t bytes, const uint64_t offset) {
Copy link
Member

Choose a reason for hiding this comment

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

This method should be const too?

Comment on lines +51 to +52
stateChange.memoryAddressValues.push_back(RegisterValue(
reinterpret_cast<const uint8_t*>(out.data()), outSize));
Copy link
Member

Choose a reason for hiding this comment

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

Personally I think RegisterValue should have proper move semantics and you should be able to create a blank register of some size and move it into the vector

uint8_t bytesRead = response->target.size;
const uint8_t* data = response->data.getAsVector<uint8_t>();
// TODO clean up malloc
uint8_t* data = (uint8_t*)malloc(bytesRead);
Copy link
Member

Choose a reason for hiding this comment

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

See comment about using scoped vector

initialStackFrame.push_back(commandLine_.size()); // argc
for (size_t i = 0; i < commandLine_.size(); i++) {
char* argvi = commandLine_[i].data();
uint8_t* argvi = reinterpret_cast<uint8_t*>(commandLine_[i].data());
Copy link
Member

Choose a reason for hiding this comment

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

use auto on lhs if rhs is reinterpret_cast

return "+sve,+lse";
#elif SIMENG_LLVM_VERSION < 18
return "+sve,+lse,+sve2,+sme,+sme-f64";
return "+sve,+lse,+sve2,+sme,+sme-f64,+sme-i64";
Copy link
Member

Choose a reason for hiding this comment

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

Should this be part of the PR?

#include <algorithm>
#include <cassert>
#include <cstring>
#include <iostream>
Copy link
Member

Choose a reason for hiding this comment

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

Remove extra header

struct safePointer {
public:
explicit safePointer(const uint8_t* ptr) : ptr(ptr) {}
explicit safePointer() : ptr(nullptr) {}
Copy link
Member

Choose a reason for hiding this comment

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

do we need a null safePointer? Is it still safe if calling anything on it is UB? also, might want to assert in the ctor above if the build is debug

@github-project-automation github-project-automation bot moved this from ToDo to Changes Requested in SimEng Development Aug 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Changes Requested

Development

Successfully merging this pull request may close these issues.

5 participants