Skip to content

Commit 9af8444

Browse files
committed
add safety annotation to unsafe enum discriminant code
1 parent 144e56d commit 9af8444

File tree

3 files changed

+41
-28
lines changed

3 files changed

+41
-28
lines changed

lu_packets_derive/src/replica_serde.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use proc_macro2::{Ident, Span, TokenStream};
22
use quote::quote;
3-
use syn::{parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Field, Fields, Generics, Lit, LitInt, Meta, NestedMeta};
3+
use syn::{
4+
Attribute, Data, DataEnum, DeriveInput, Field, Fields, Lit, LitInt, Meta, NestedMeta,
5+
parse_macro_input, parse_quote,
6+
};
47

58
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
69
let input = parse_macro_input!(input as DeriveInput);
@@ -10,14 +13,14 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1013
match &input.data {
1114
Data::Struct(data) => {
1215
deser_code = gen_deser_code_struct(&data.fields);
13-
ser_code = gen_ser_code_struct(&data.fields, &name);
14-
},
16+
ser_code = gen_ser_code_struct(&data.fields, name);
17+
}
1518
Data::Enum(data) => {
1619
let ty = get_enum_type(&input);
1720
let pre_disc_padding = get_pre_disc_padding(&input);
1821
let post_disc_padding = get_post_disc_padding(&input);
19-
deser_code = gen_deser_code_enum(data, &name, &ty, &pre_disc_padding, &post_disc_padding);
20-
ser_code = gen_ser_code_enum(data, &name, &ty, &pre_disc_padding, &post_disc_padding, &input.generics);
22+
deser_code = gen_deser_code_enum(data, name, &ty, &pre_disc_padding, &post_disc_padding);
23+
ser_code = gen_ser_code_enum(data, name, &ty, &pre_disc_padding, &post_disc_padding);
2124
}
2225
Data::Union(_) => unimplemented!(),
2326
}
@@ -182,7 +185,7 @@ fn gen_ser_code_struct(fields: &Fields, name: &Ident) -> TokenStream {
182185
}
183186
}
184187

185-
fn gen_ser_code_enum(data: &DataEnum, name: &Ident, ty: &Ident, pre_disc_padding: &Option<LitInt>, post_disc_padding: &Option<LitInt>, generics: &Generics) -> TokenStream {
188+
fn gen_ser_code_enum(data: &DataEnum, name: &Ident, ty: &Ident, pre_disc_padding: &Option<LitInt>, post_disc_padding: &Option<LitInt>) -> TokenStream {
186189
let mut arms = vec![];
187190
for f in &data.variants {
188191
let ident = &f.ident;
@@ -194,7 +197,7 @@ fn gen_ser_code_enum(data: &DataEnum, name: &Ident, ty: &Ident, pre_disc_padding
194197
let write_post_padding = gen_write_padding(post_disc_padding);
195198
quote! {
196199
#write_pre_padding
197-
let disc = unsafe { *(self as *const #name #generics as *const #ty) };
200+
let disc = unsafe { *::std::ptr::from_ref(self).cast::<#ty>() };
198201
::endio::LEWrite::write(writer, disc)?;
199202
#write_post_padding
200203
match self {

src/auth/client/mod.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
//! Client-received auth messages.
2-
use std::io::{Error, ErrorKind::InvalidData, Result as Res, Read};
2+
use std::{
3+
io::{Error, ErrorKind::InvalidData, Read, Result as Res},
4+
ptr,
5+
};
36

4-
use endio::{LEWrite, LERead, Deserialize, Serialize};
5-
use endio::LittleEndian as LE;
6-
use lu_packets_derive::MessageFromVariants;
7-
use lu_packets_derive::VariantTests;
7+
use endio::{Deserialize, LERead, LEWrite, LittleEndian as LE, Serialize};
8+
use lu_packets_derive::{MessageFromVariants, VariantTests};
89

9-
use crate::common::{LuString3, LuString33, LuString37, LuVarWString, LuWString33, ServiceId};
10-
use crate::general::client::{DisconnectNotify, Handshake, GeneralMessage};
11-
use crate::world::server::Language;
10+
use crate::{
11+
common::{LuString3, LuString33, LuString37, LuVarWString, LuWString33, ServiceId},
12+
general::client::{DisconnectNotify, GeneralMessage, Handshake},
13+
world::server::Language,
14+
};
1215

1316
/// All messages that can be received by a client from an auth server.
1417
pub type Message = crate::raknet::client::Message<LuMessage>;
@@ -128,7 +131,10 @@ where
128131
&'a LuVarWString<u16>: Serialize<LE, W>,
129132
{
130133
fn serialize(self, writer: &mut W) -> Res<()> {
131-
let disc = unsafe { *(self as *const LoginResponse as *const u8) };
134+
// SAFETY: Because `LoginResponse` is marked `repr(u8)`, its layout is a `repr(C)` `union`
135+
// between `repr(C)` structs, each of which has the `u8` discriminant as its first
136+
// field, so we can read the discriminant without offsetting the pointer.
137+
let disc = unsafe { *ptr::from_ref(self).cast::<u8>() };
132138
writer.write(disc)?;
133139
match self {
134140
LoginResponse::Ok { events, version, session_key, redirect_address, chat_server_address, cdn_key, cdn_ticket, language, country_code, just_upgraded_from_ftp, is_ftp, time_remaining_in_ftp, stamps } => {

src/world/client/mod.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
//! Client-received world messages.
2-
use std::io::{Error, ErrorKind::InvalidData, Read, Write};
3-
use std::io::Result as Res;
2+
use std::{
3+
io::{Error, ErrorKind::InvalidData, Read, Result as Res, Write},
4+
ptr,
5+
};
46

5-
use endio::{Deserialize, LERead, LEWrite, Serialize};
6-
use endio::LittleEndian as LE;
7+
use endio::{Deserialize, LERead, LEWrite, LittleEndian as LE, Serialize};
78
use lu_packets_derive::{MessageFromVariants, VariantTests};
89

9-
use crate::chat::ChatChannel;
10-
use crate::chat::client::ChatMessage;
11-
use crate::common::{ObjId, LuString33, LuWString33, LuWString42, LVec, ServiceId};
12-
use crate::general::client::{DisconnectNotify, Handshake, GeneralMessage};
13-
use super::{Lot, lnv::LuNameValue, Vector3, ZoneId};
14-
use super::gm::client::SubjectGameMessage;
10+
use super::{Lot, Vector3, ZoneId, gm::client::SubjectGameMessage, lnv::LuNameValue};
11+
use crate::{
12+
chat::{ChatChannel, client::ChatMessage},
13+
common::{LVec, LuString33, LuWString33, LuWString42, ObjId, ServiceId},
14+
general::client::{DisconnectNotify, GeneralMessage, Handshake},
15+
};
1516

1617
/// All messages that can be received by a client from a world server.
1718
pub type Message = crate::raknet::client::Message<LuMessage>;
@@ -378,9 +379,12 @@ impl<R: Read> Deserialize<LE, R> for AddFriendResponse {
378379
}
379380
}
380381

381-
impl<'a, W: Write> Serialize<LE, W> for &'a AddFriendResponse {
382+
impl<W: Write> Serialize<LE, W> for &AddFriendResponse {
382383
fn serialize(self, writer: &mut W) -> Res<()> {
383-
let disc = unsafe { *(&self.response_type as *const AddFriendResponseType as *const u8) };
384+
// SAFETY: Because `AddFriendResponseType` is marked `repr(u8)`, its layout is a `repr(C)` `union`
385+
// between `repr(C)` structs, each of which has the `u8` discriminant as its first
386+
// field, so we can read the discriminant without offsetting the pointer.
387+
let disc = unsafe { *ptr::from_ref(self).cast::<u8>() };
384388
LEWrite::write(writer, disc)?;
385389
let mut is_online_x = &false;
386390
let mut sender_id_x = &0;

0 commit comments

Comments
 (0)