1use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
2
3use rustc_abi::Size;
4use rustc_target::spec::{Env, Os};
5
6use crate::*;
7
8impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
9pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
10 fn getaddrinfo(
11 &mut self,
12 node: &OpTy<'tcx>,
13 service: &OpTy<'tcx>,
14 hints: &OpTy<'tcx>,
15 res: &OpTy<'tcx>,
16 ) -> InterpResult<'tcx, Scalar> {
17 let this = self.eval_context_mut();
18
19 let node_ptr = this.read_pointer(node)?;
20 let service_ptr = this.read_pointer(service)?;
21 let hints_ptr = this.read_pointer(hints)?;
22 let res_mplace = this.deref_pointer(res)?;
23
24 if this.ptr_is_null(node_ptr)? {
25 throw_unsup_format!(
28 "getaddrinfo: getting the address info without a `node` is unsupported"
29 );
30 }
31
32 let mut port = 0;
33 if !this.ptr_is_null(service_ptr)? {
34 let service_c_str = this.read_c_str(service_ptr)?;
37
38 match str::from_utf8(service_c_str).ok().and_then(|s| s.parse::<u16>().ok()) {
40 Some(service_port) => port = service_port,
41 None => {
42 throw_unsup_format!(
46 "getaddrinfo: non-numeric `service` arguments aren't supported"
47 )
48 }
49 }
50 }
51
52 let node_c_str = this.read_c_str(node_ptr)?;
53 let Some(node_str) = str::from_utf8(node_c_str).ok() else {
54 throw_unsup_format!("getaddrinfo: node is not a valid UTF-8 string")
55 };
56
57 if this.ptr_is_null(hints_ptr)? {
58 throw_unsup_format!(
61 "getaddrinfo: getting address info without providing socket type hint is unsupported"
62 )
63 }
64
65 let hints_layout = this.libc_ty_layout("addrinfo");
66 let hints_mplace = this.ptr_to_mplace(hints_ptr, hints_layout);
67
68 let family_field = this.project_field_named(&hints_mplace, "ai_family")?;
69 let family = this.read_scalar(&family_field)?;
70 if family != Scalar::from_i32(0) {
71 throw_unsup_format!("getaddrinfo: family hints are not supported")
73 }
74
75 let socktype_field = this.project_field_named(&hints_mplace, "ai_socktype")?;
76 let socktype = this.read_scalar(&socktype_field)?;
77 if socktype != this.eval_libc("SOCK_STREAM") {
78 throw_unsup_format!(
80 "getaddrinfo: only queries with socket type SOCK_STREAM are supported"
81 )
82 }
83
84 let protocol_field = this.project_field_named(&hints_mplace, "ai_protocol")?;
85 let protocol = this.read_scalar(&protocol_field)?;
86 if protocol != Scalar::from_i32(0) {
87 throw_unsup_format!("getaddrinfo: protocol hints are not supported")
89 }
90
91 let flags_field = this.project_field_named(&hints_mplace, "ai_flags")?;
92 let flags = this.read_scalar(&flags_field)?;
93 if flags != Scalar::from_i32(0) {
94 throw_unsup_format!("getaddrinfo: flag hints are not supported")
96 }
97
98 let socket_addrs = match (node_str, port).to_socket_addrs() {
99 Ok(addrs) => addrs,
100 Err(e) => {
101 this.emit_diagnostic(NonHaltingDiagnostic::SocketAddressResolution { error: e });
106 this.set_last_error(LibcError("EPROTO"))?;
107 return interp_ok(this.eval_libc("EAI_SYSTEM"));
108 }
109 };
110
111 let res_ptr = this.allocate_address_infos(socket_addrs)?;
112
113 this.write_pointer(res_ptr, &res_mplace)?;
114 interp_ok(Scalar::from_i32(0))
115 }
116
117 fn freeaddrinfo(&mut self, res: &OpTy<'tcx>) -> InterpResult<'tcx> {
118 let this = self.eval_context_mut();
119
120 let res_ptr = this.read_pointer(res)?;
121
122 this.free_address_infos(res_ptr)
123 }
124
125 fn read_socklen(
128 &self,
129 op: &impl Projectable<'tcx, Provenance>,
130 ) -> InterpResult<'tcx, Option<u64>> {
131 let ecx = self.eval_context_ref();
132
133 let layout = op.layout();
134 let scalar = ecx.read_scalar(op)?;
135 let len = if layout.backend_repr.is_signed() {
136 u64::try_from(scalar.to_int(layout.size)?).ok()
137 } else {
138 u64::try_from(scalar.to_uint(layout.size)?).ok()
139 };
140 interp_ok(len)
141 }
142
143 fn read_socket_address(
147 &self,
148 address: &OpTy<'tcx>,
149 address_len: &OpTy<'tcx>,
150 foreign_name: &'static str,
151 ) -> InterpResult<'tcx, Result<SocketAddr, IoError>> {
152 let this = self.eval_context_ref();
153
154 let Some(address_len) = this.read_socklen(address_len)? else {
157 return interp_ok(Err(LibcError("EINVAL")));
158 };
159 let address = this.deref_pointer(address)?;
160
161 this.check_ptr_access(
163 address.ptr(),
164 Size::from_bytes(address_len),
165 CheckInAllocMsg::MemoryAccess,
166 )?;
167
168 if address_len < address.layout.size.bytes() {
170 return interp_ok(Err(LibcError("EINVAL")));
172 }
173
174 let family_field = this.project_field_named(&address, "sa_family")?;
175 let family_layout = this.libc_ty_layout("sa_family_t");
176 let family = this.read_scalar(&family_field)?.to_int(family_layout.size)?;
177
178 let socket_addr = if family == this.eval_libc_i32("AF_INET").into() {
181 let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
182 if address_len != sockaddr_in_layout.size.bytes() {
183 return interp_ok(Err(LibcError("EINVAL")));
185 }
186 let address = this.ptr_to_mplace(address.ptr(), sockaddr_in_layout);
187
188 let port_field = this.project_field_named(&address, "sin_port")?;
189 let port_bytes: [u8; 2] = this
191 .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
192 .try_into()
193 .unwrap();
194 let port = u16::from_be_bytes(port_bytes);
195
196 let addr_field = this.project_field_named(&address, "sin_addr")?;
197 let s_addr_field = this.project_field_named(&addr_field, "s_addr")?;
198 let addr_bytes: [u8; 4] = this
200 .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(4))?
201 .try_into()
202 .unwrap();
203 let addr_bits = u32::from_be_bytes(addr_bytes);
204
205 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from_bits(addr_bits), port))
209 } else if family == this.eval_libc_i32("AF_INET6").into() {
210 let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
211 if address_len != sockaddr_in6_layout.size.bytes() {
212 return interp_ok(Err(LibcError("EINVAL")));
214 }
215 let address = this.ptr_to_mplace(address.ptr(), sockaddr_in6_layout);
216
217 let port_field = this.project_field_named(&address, "sin6_port")?;
218 let port_bytes: [u8; 2] = this
220 .read_bytes_ptr_strip_provenance(port_field.ptr(), Size::from_bytes(2))?
221 .try_into()
222 .unwrap();
223 let port = u16::from_be_bytes(port_bytes);
224
225 let addr_field = this.project_field_named(&address, "sin6_addr")?;
226 let s_addr_field = this
227 .project_field_named(&addr_field, "s6_addr")?
228 .transmute(this.machine.layouts.u128, this)?;
229 let addr_bytes: [u8; 16] = this
231 .read_bytes_ptr_strip_provenance(s_addr_field.ptr(), Size::from_bytes(16))?
232 .try_into()
233 .unwrap();
234 let addr_bits = u128::from_be_bytes(addr_bytes);
235
236 let flowinfo_field = this.project_field_named(&address, "sin6_flowinfo")?;
237 let flowinfo = this.read_scalar(&flowinfo_field)?.to_u32()?;
240
241 let scope_id_field = this.project_field_named(&address, "sin6_scope_id")?;
242 let scope_id = this.read_scalar(&scope_id_field)?.to_u32()?;
245
246 SocketAddr::V6(SocketAddrV6::new(
249 Ipv6Addr::from_bits(addr_bits),
250 port,
251 flowinfo,
252 scope_id,
253 ))
254 } else {
255 throw_unsup_format!(
258 "{foreign_name}: address family {family:#x} is unsupported, \
259 only AF_INET and AF_INET6 are allowed"
260 );
261 };
262
263 interp_ok(Ok(socket_addr))
264 }
265
266 fn write_socket_address(
280 &mut self,
281 address: Option<&SocketAddr>,
282 address_ptr: Pointer,
283 address_len_place: &MPlaceTy<'tcx>,
284 ) -> InterpResult<'tcx> {
285 let this = self.eval_context_mut();
286
287 if address.is_none() && this.ptr_is_null(address_len_place.ptr())? {
288 return interp_ok(());
290 }
291
292 let address_buffer_len: u64 = this
301 .read_scalar(address_len_place)?
302 .to_int(address_len_place.layout.size)?
303 .try_into()
304 .unwrap();
305
306 this.check_ptr_access(
308 address_ptr,
309 Size::from_bytes(address_buffer_len),
310 CheckInAllocMsg::MemoryAccess,
311 )?;
312
313 let Some(address) = address else {
314 return this.write_int(0, address_len_place);
317 };
318
319 let address_buffer = match address {
320 SocketAddr::V4(address) => {
321 let address_bytes = address.ip().octets();
323 let port = address.port().to_be();
325
326 let sockaddr_in_layout = this.libc_ty_layout("sockaddr_in");
327 let address_buffer = this.allocate(sockaddr_in_layout, MemoryKind::Stack)?;
331
332 let sin_family_field = this.project_field_named(&address_buffer, "sin_family")?;
333 let af_inet = this.eval_libc("AF_INET");
338 let address_family =
339 Scalar::from_int(af_inet.to_int(af_inet.size())?, sin_family_field.layout.size);
340 this.write_scalar(address_family, &sin_family_field)?;
341
342 let sin_port_field = this.project_field_named(&address_buffer, "sin_port")?;
343 this.write_bytes_ptr(sin_port_field.ptr(), port.to_ne_bytes())?;
346
347 let sin_addr_field = this.project_field_named(&address_buffer, "sin_addr")?;
348 let s_addr_field = this.project_field_named(&sin_addr_field, "s_addr")?;
349 this.write_bytes_ptr(s_addr_field.ptr(), address_bytes)?;
350
351 let sin_zero_field = this.project_field_named(&address_buffer, "sin_zero")?;
355 let mut sin_zero_elems = this.project_array_fields(&sin_zero_field)?;
356 while let Some((_idx, elem)) = sin_zero_elems.next(this)? {
357 this.write_int(0, &elem)?;
358 }
359
360 if matches!(this.tcx.sess.target.os, Os::FreeBsd | Os::MacOs) {
362 let sin_len_field = this.project_field_named(&address_buffer, "sin_len")?;
363 this.write_int(sockaddr_in_layout.size.bytes(), &sin_len_field)?;
364 }
365
366 address_buffer
367 }
368 SocketAddr::V6(address) => {
369 let address_bytes = address.ip().octets();
371 let port = address.port().to_be();
373 let flowinfo = address.flowinfo();
375 let scope_id = address.scope_id();
377
378 let sockaddr_in6_layout = this.libc_ty_layout("sockaddr_in6");
379 let address_buffer = this.allocate(sockaddr_in6_layout, MemoryKind::Stack)?;
383
384 let sin6_family_field = this.project_field_named(&address_buffer, "sin6_family")?;
385 let af_inet6 = this.eval_libc("AF_INET6");
390 let address_family = Scalar::from_int(
391 af_inet6.to_int(af_inet6.size())?,
392 sin6_family_field.layout.size,
393 );
394 this.write_scalar(address_family, &sin6_family_field)?;
395
396 let sin6_port_field = this.project_field_named(&address_buffer, "sin6_port")?;
397 this.write_bytes_ptr(sin6_port_field.ptr(), port.to_ne_bytes())?;
400
401 let sin6_flowinfo_field =
402 this.project_field_named(&address_buffer, "sin6_flowinfo")?;
403 this.write_scalar(Scalar::from_u32(flowinfo), &sin6_flowinfo_field)?;
404
405 let sin6_scope_id_field =
406 this.project_field_named(&address_buffer, "sin6_scope_id")?;
407 this.write_scalar(Scalar::from_u32(scope_id), &sin6_scope_id_field)?;
408
409 let sin6_addr_field = this.project_field_named(&address_buffer, "sin6_addr")?;
410 let s6_addr_field = this.project_field_named(&sin6_addr_field, "s6_addr")?;
411 this.write_bytes_ptr(s6_addr_field.ptr(), address_bytes)?;
412
413 if matches!(this.tcx.sess.target.os, Os::Solaris | Os::Illumos) {
415 let sin6_src_id_field =
416 this.project_field_named(&address_buffer, "__sin6_src_id")?;
417 this.write_int(0, &sin6_src_id_field)?;
418 } else if matches!(this.tcx.sess.target.os, Os::FreeBsd | Os::MacOs) {
419 let sin6_len_field = this.project_field_named(&address_buffer, "sin6_len")?;
420 this.write_int(sockaddr_in6_layout.size.bytes(), &sin6_len_field)?;
421 }
422
423 address_buffer
424 }
425 };
426
427 this.mem_copy(
429 address_buffer.ptr(),
430 address_ptr,
431 address_buffer.layout.size.min(Size::from_bytes(address_buffer_len)),
433 true,
436 )?;
437 this.deallocate_ptr(address_buffer.ptr(), None, MemoryKind::Stack)?;
440
441 this.write_int(address_buffer.layout.size.bytes(), address_len_place)?;
443
444 interp_ok(())
445 }
446}
447
448impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
449trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
450 fn allocate_address_infos(
453 &mut self,
454 mut addresses: impl Iterator<Item = SocketAddr>,
455 ) -> InterpResult<'tcx, Pointer> {
456 let this = self.eval_context_mut();
457
458 let Some(address) = addresses.next() else {
459 return interp_ok(Pointer::null());
461 };
462
463 let addrinfo_layout = this.libc_ty_layout("addrinfo");
464
465 let addrinfo_mplace =
466 this.allocate(addrinfo_layout, MiriMemoryKind::SocketAddress.into())?;
467
468 let flags_mplace = this.project_field_named(&addrinfo_mplace, "ai_flags")?;
469 let flags = if matches!(this.tcx.sess.target.env, Env::Gnu) {
474 this.eval_libc_i32("AI_V4MAPPED") | this.eval_libc_i32("AI_ADDRCONFIG")
475 } else {
476 0
477 };
478 this.write_int(flags, &flags_mplace)?;
479
480 let family_mplace = this.project_field_named(&addrinfo_mplace, "ai_family")?;
481 let family = match &address {
482 SocketAddr::V4(_) => this.eval_libc("AF_INET"),
483 SocketAddr::V6(_) => this.eval_libc("AF_INET6"),
484 };
485 this.write_scalar(family, &family_mplace)?;
486
487 let socktype_mplace = this.project_field_named(&addrinfo_mplace, "ai_socktype")?;
488 this.write_scalar(this.eval_libc("SOCK_STREAM"), &socktype_mplace)?;
489
490 let protocol_mplace = this.project_field_named(&addrinfo_mplace, "ai_protocol")?;
491 this.write_int(0, &protocol_mplace)?;
494
495 let sockaddr_layout = this.libc_ty_layout("sockaddr_storage");
497
498 let addrlen_mplace = this.project_field_named(&addrinfo_mplace, "ai_addrlen")?;
499 let addr_mplace = this.project_field_named(&addrinfo_mplace, "ai_addr")?;
500 this.write_int(sockaddr_layout.size.bytes(), &addrlen_mplace)?;
501
502 let sockaddr_mplace = this.allocate(sockaddr_layout, MiriMemoryKind::Machine.into())?;
503 this.write_socket_address(Some(&address), sockaddr_mplace.ptr(), &addrlen_mplace)?;
504 this.write_pointer(sockaddr_mplace.ptr(), &addr_mplace)?;
505
506 let canonname_mplace = this.project_field_named(&addrinfo_mplace, "ai_canonname")?;
507 this.write_pointer(Pointer::null(), &canonname_mplace)?;
508
509 let next_mplace = this.project_field_named(&addrinfo_mplace, "ai_next")?;
511 let next_ptr = this.allocate_address_infos(addresses)?;
512 this.write_pointer(next_ptr, &next_mplace)?;
513
514 interp_ok(addrinfo_mplace.ptr())
515 }
516
517 fn free_address_infos(&mut self, address_ptr: Pointer) -> InterpResult<'tcx> {
520 let this = self.eval_context_mut();
521
522 if this.ptr_is_null(address_ptr)? {
523 return interp_ok(());
525 }
526
527 let addrinfo_layout = this.libc_ty_layout("addrinfo");
528 let addrinfo_mplace = this.ptr_to_mplace(address_ptr, addrinfo_layout);
529
530 let addr_field = this.project_field_named(&addrinfo_mplace, "ai_addr")?;
531 let addr_ptr = this.read_pointer(&addr_field)?;
532 this.deallocate_ptr(addr_ptr, None, MiriMemoryKind::Machine.into())?;
533
534 let next_field = this.project_field_named(&addrinfo_mplace, "ai_next")?;
535 let next_ptr = this.read_pointer(&next_field)?;
536 this.free_address_infos(next_ptr)?;
537
538 this.deallocate_ptr(address_ptr, None, MiriMemoryKind::SocketAddress.into())?;
539
540 interp_ok(())
541 }
542}