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

[SR-12088] Cannot create pointer to 0-length C struct field in Swift #54524

Open
woolsweater opened this issue Jan 25, 2020 · 3 comments
Open
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior.

Comments

@woolsweater
Copy link

Previous ID SR-12088
Radar rdar://problem/58999132
Original Reporter @woolsweater
Type Bug

Attachment: Download

Environment

Apple Swift version 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15)
Target: x86_64-apple-darwin18.6.0

Additional Detail from JIRA
Votes 0
Component/s
Labels Bug
Assignee None
Priority Medium

md5: 99c1361955b620d095fc55793eaa91e4

Issue Description:

Given a C struct with a trailing member that is a 0-length array*:

typedef struct {
    size_t len;
    int8_t payload[0];
} Foo;

Swift cannot produce a valid pointer to the payload field.

Note first,

let offset = MemoryLayout<Foo>.offset(of: \Foo.payload)!
assert(offset == 0)

I have two helper functions in C to aid inspection (lldb seems to sometimes have trouble recognizing Swift pointer variables):

// Just `printf("%p\n", p);`
void whats_this_addr(void * p);
  
// Wrapper around memcpy, allowing inspection of the addresses
// and/or memory in the debugger
void my_memcpy(void * dest, const void * src, size_t len);

Then, working with a heap-allocated instance of the struct in Swift:

typealias FooRef = UnsafeMutablePointer<Foo>
let new: FooRef =  ... // Allocate via `UnsafeMutableRawPointer` and then `bindMemory`
let payloadStart = UnsafeMutableRawPointer(&new.pointee.payload)
whats_this_addr(payloadStart)    // Garbage value, consistently somewhere above `new`

// Curiouser
let payload: [Int8] = ...
_ = payload.withUnsafeBytes {
    my_memcpy(payloadStart, $0.baseAddress!, payload.count)    // In a debug build, the first arg is 0x7ff... inside my_memcpy
}

// And curiouser
whats_this_addr(&new.pointee.payload)    // Garbage, _different_ from whats_this_addr(payloadStart) above -- consistently somewhere below `new`
whats_this_addr(&new.pointee.payload)    // NULL
whats_this_addr(payloadStart)            // NULL?!

If the offset/pointer is calculated in C, Swift can use it perfectly well. The problem seems to be solely with Swift's calculation of the location of the field.

The address range is different in a release build (tested with -fno-pie), but the behavior is effectively the same.

Full running example attached, also available at https://github.com/woolsweater/flex-arr-mem , "bug-report" branch (there is a fuller example on master).

*In lieu of a true flexible array member int8_t payload[], which isn't imported at all. (Swift does handle a member with declared length 1 correctly, however.)

@CodaFi
Copy link
Member

CodaFi commented Jan 27, 2020

The problem is deeper than that. The Clang Importer imports constant-sized arrays by mapping them directly to a TupleType with matching cardinality. So, a VLA is actually being imported as (), which has no size under our ABI. And, since we lack the global rule C and C++ have about always being to take the address of a field we have no formal reason to reserve space for it.

Now, is this a bug is the question. Zero-sized arrays like this for trailing storage are a GNU C extension, and we have not historically gone out of our way to support these kinds of language extensions. This feels like a point to raise on the Swift forums.

@woolsweater
Copy link
Author

since we lack the global rule C and C++ have about always being to take the address of a field

Ah, even when the layout is defined in C? Is that deliberate or "not implemented yet"?

a GNU C extension

Right, understood. I ran into this because I actually wanted a flexible array member int8_t payload[]; which, as noted, is not imported at all. I figured this was worth filing given that the length-1 version will work as expected.

Since flexible array members are part of the C standard, I suppose that's the real question I should post about – i.e., how can Swift support them?

@beccadax
Copy link
Contributor

@swift-ci create

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior.
Projects
None yet
Development

No branches or pull requests

3 participants