4

In the code below could somebody perhaps explain what is happening on the line struct ether_header *eh = (struct ether_header *) sendbuf;? I understand that it is creating a pointer eh of type ether_header and on the RHS you are casting sendbuf to be a pointer of tyoe struct ether_header. But how can you do this is sendbuf is a char array? Also why would you do this?

Here is the link to the full code send ethernet frame

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>

int main(int argc, char *argv[])
{
    int sockfd;
    struct ifreq if_idx;
    struct ifreq if_mac;
    int tx_len = 0;
    char sendbuf[BUF_SIZ];
    struct ether_header *eh = (struct ether_header *) sendbuf;
netleap tom
  • 143
  • 4
  • 10

2 Answers2

3

But how can you do this isif sendbuf is a char array?

Code should not do this.

Casting a pointer to a type that was not originally a valid pointer for that type is undefined behavior (UB).

char sendbuf[BUF_SIZ];
struct ether_header *eh = (struct ether_header *) sendbuf;  // UB

At a minimum, consider if struct ether_header had an alignment requirement to be an even address and sendbuf[] began on an odd address. The assignment may kill the program.

A 2nd concern is what unposted code might later do with sendbuf[] and eh which can violating strict aliasing rule @Andrew Henle.


A better approach is to use a union. Now the members are aligned and the union handles the strict aliasing rule.

union {
  char sendbuf[BUF_SIZ];
  struct ether_header eh;
} u;

Also why would you do this?

To allow access to the data from 2 data type perspectives. Perhaps to make a data dump of u.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Or just do it the other way round: `struct ether_header eh; char * sendbuf = (char*) &eh;`? – alk Jan 04 '18 at 17:14
  • @alk That handles the alignment issue. Yet it appears `sendbuf[]` may have size requirements exceeding `sizeof(eh)`. OP is not clear on this point. Further, given anti-aliasing/optimizations, changes in `eh` are detectable in `sendbuf`, but perhaps not the other way around. That AA rule has subtleties that code can avoided with a `union`. – chux - Reinstate Monica Jan 04 '18 at 17:22
  • *Casting a pointer to a type that was not originally a valid pointer for that type is undefined behavior* - standard says so? or where can someone learn this? – user2736738 Jan 04 '18 at 17:33
  • @coderredoc That is derived. If the code does something that the C spec does not specify, it is UB. "or by the omission of any explicit definition of behavior." C11 §4 2. So the issue becomes, where is the C spec does it say code is allowed to cast _any_ `char *` pointer to `struct some_struct *`? As that is lacking, code is UB. – chux - Reinstate Monica Jan 04 '18 at 17:37
  • Well ... I always struggle with this pointer conversion. What I have: C11 (draft) 6.3.2.1p7: "*A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. ...*" – alk Jan 04 '18 at 17:40
  • "*... When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object [...]*" @coderredoc – alk Jan 04 '18 at 17:40
  • @alk The `union` idiom handles the alignment issues of [the comment](https://stackoverflow.com/questions/48099028/casting-a-char-array-to-be-of-type-struct/48100169?noredirect=1#comment83176517_48100169). The AA part is subtle. Covered well in [this good answer](https://stackoverflow.com/a/99010/2410359) – chux - Reinstate Monica Jan 04 '18 at 17:47
  • The truth is that one *may* convert pointers, the ugly thing is that there *might* be subtle pitfalls provoking UB when *accessing* the data via a pointer of "wrong" type. @coderredoc – alk Jan 04 '18 at 17:49
  • The only case generally allowed is to access any object (inside its bounds) bytewise via a `char`-pointer. @coderredoc (like proposed by [this comment](https://stackoverflow.com/questions/48099028/casting-a-char-array-to-be-of-type-struct#comment83175674_48100169)). – alk Jan 04 '18 at 17:50
  • 1
    @alk I suspect it is any character pointer. A pointer to `unsigned char` has the advantage of no traps. – chux - Reinstate Monica Jan 04 '18 at 17:52
  • @alk.: Thanks to you and chux both. Will get back to this answer ..I am just going through few things regarding this. Will get back if I have to know something else. – user2736738 Jan 04 '18 at 18:37
  • Hi all, I provided a link to the full code as from reading your comments it is important, thanks – netleap tom Jan 05 '18 at 10:05
0

The line char sendbuf[BUF_SIZ] allocates a block of chars (i.e. bytes on most systems) and the cast struct ether_header *eh = (struct ether_header *) sendbuf says that you explicitly want to treat this as a struct ether_header type. There are no significant instructions from this cast, aside from (possibly) setting a CPU register.

You'll end up with two pointers to the same block of memory. Modification of one will affect the other.

That being said, it is not completely correct/safe, because the sendbuf may not be appropriately aligned to actually contain a struct ether_header.

Edit: In regard to struct aliasing rules a char* is explicitly allowed to alias any other data type, but the reverse is not necessarily true.

Joe Hickey
  • 810
  • 7
  • 8
  • 7
    "However, it is not in violation of aliasing rules since a char* is explicitly allowed to alias any other data type." This is not a case of a `char *` aliasing another data type, this is a case of another data type aliasing a `char *`. – Andrew Henle Jan 04 '18 at 16:16
  • 1
    If `struct ether_header *` has stronger alignment requirements than `char *` `struct ether_header *eh = (struct ether_header *) sendbuf` can cause a problem. This assignment is UB. – chux - Reinstate Monica Jan 04 '18 at 16:46
  • @AndrewHenle simply casting the pointers creates an alias both ways. Whether it violates strict aliasing rules depends on how they are dereferenced, i.e. whether one is writing to the `char` and then reading the struct vs. writing to the struct and then reading the `char`. The OP hasn't indicated how these pointers are *used* so we can't actually see if there are strict aliasing violations here, really. – Joe Hickey Jan 04 '18 at 18:13
  • @chux, can you clarify whether the *assignment itself* is UB or whether the actual *use* of the alias is UB. – MFisherKDX Jan 04 '18 at 19:35
  • @MFisherKDX [Both](https://stackoverflow.com/questions/48099028/casting-a-char-array-to-be-of-type-struct/48099256?noredirect=1#comment83179922_48099256). Note it is even worse, before the assignment, the cast itself is UB such as `(struct ether_header *) sendbuf;` – chux - Reinstate Monica Jan 04 '18 at 19:46
  • 2
    *The OP hasn't indicated how these pointers are used so we can't actually see if there are strict aliasing violations here, really.* No, it is a strict aliasing violation. What we can't see is if the resulting UB causes problems. Programmers who have only coded on x86 platforms tend not to understand the problems caused by violations of strict aliasing and other types of alignment restrictions on different data types. x86 is *very* forgiving any type of unaligned memory access. [Other hardware? Not so much.](https://www.google.com/search?q=SIGBUS+SPARC) – Andrew Henle Jan 04 '18 at 19:50
  • Please note that I'm making a distinction between the potential *aliasing violation* from the potential *alignment violation*. These are different violations. Aliasing violations occur when the type-punned pointers are dereferenced, not when the type-punned pointers are created/assigned. – Joe Hickey Jan 04 '18 at 21:25