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

Fix issues around dicom_ul::get_client_pdu #590

Merged
merged 3 commits into from
Nov 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions scpproxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ dicom-dictionary-std = { path = "../dictionary-std/", version = "0.7.0" }
snafu = "0.8"
tracing = "0.1.34"
tracing-subscriber = "0.3.11"
bytes = "1.6"
13 changes: 8 additions & 5 deletions scpproxy/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use bytes::BytesMut;
use clap::{crate_version, value_parser, Arg, ArgAction, Command};
use dicom_ul::association::client::get_client_pdu;
use dicom_ul::pdu::writer::write_pdu;
use dicom_ul::pdu::Pdu;
use dicom_ul::association::client::get_client_pdu;
use snafu::{Backtrace, OptionExt, Report, ResultExt, Snafu, Whatever};
use std::io::Write;
use std::net::{Shutdown, TcpListener, TcpStream};
Expand Down Expand Up @@ -93,10 +94,11 @@ fn run(

{
let mut reader = scu_stream.try_clone().context(CloneSocketSnafu)?;
let mut buf = BytesMut::with_capacity(max_pdu_length as usize);
let message_tx = message_tx.clone();
scu_reader_thread = thread::spawn(move || {
loop {
match get_client_pdu(&mut reader, max_pdu_length, strict) {
match get_client_pdu(&mut reader, &mut buf, max_pdu_length, strict) {
Ok(pdu) => {
message_tx
.send(ThreadMessage::SendPdu {
Expand All @@ -105,7 +107,7 @@ fn run(
})
.context(SendMessageSnafu)?;
}
Err(dicom_ul::association::client::Error::ReceiveResponse{ .. }) => {
Err(dicom_ul::association::client::Error::ConnectionClosed) => {
message_tx
.send(ThreadMessage::Shutdown {
initiator: ProviderType::Scu,
Expand All @@ -131,9 +133,10 @@ fn run(

{
let mut reader = scp_stream.try_clone().context(CloneSocketSnafu)?;
let mut buf = BytesMut::with_capacity(max_pdu_length as usize);
scp_reader_thread = thread::spawn(move || {
loop {
match get_client_pdu(&mut reader, max_pdu_length, strict) {
match get_client_pdu(&mut reader, &mut buf, max_pdu_length, strict) {
Ok(pdu) => {
message_tx
.send(ThreadMessage::SendPdu {
Expand All @@ -142,7 +145,7 @@ fn run(
})
.context(SendMessageSnafu)?;
}
Err(dicom_ul::association::client::Error::ReceiveResponse{ .. }) => {
Err(dicom_ul::association::client::Error::ConnectionClosed) => {
message_tx
.send(ThreadMessage::Shutdown {
initiator: ProviderType::Scp,
Expand Down
33 changes: 22 additions & 11 deletions ul/src/association/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,19 @@

pub type Result<T, E = Error> = std::result::Result<T, E>;

/// Helper function to get a PDU from a reader
pub fn get_client_pdu<R: Read>(reader: &mut R, max_pdu_length: u32, strict: bool) -> Result<Pdu> {
// Receive response

let mut read_buffer = BytesMut::with_capacity(MAXIMUM_PDU_SIZE as usize);
/// Helper function to get a PDU from a reader.
///
/// Chunks of data are read into `read_buffer`,
/// which should be passed in subsequent calls
/// to receive more PDUs from the same stream.
pub fn get_client_pdu<R>(reader: &mut R, read_buffer: &mut BytesMut, max_pdu_length: u32, strict: bool) -> Result<Pdu>
where
R: Read,
{
let mut reader = BufReader::new(reader);

let msg = loop {
let mut buf = Cursor::new(&read_buffer[..]);
// try to read a PDU according to what's in the buffer
match read_pdu(&mut buf, max_pdu_length, strict).context(ReceiveResponseSnafu)? {
Some(pdu) => {
read_buffer.advance(buf.position() as usize);
Expand All @@ -167,11 +171,11 @@
let recv = reader
.fill_buf()
.context(ReadPduSnafu)
.context(ReceiveSnafu)?
.to_vec();
reader.consume(recv.len());
.context(ReceiveSnafu)?;
let bytes_read = recv.len();
read_buffer.extend_from_slice(&recv);

Check warning on line 176 in ul/src/association/client.rs

View workflow job for this annotation

GitHub Actions / Test (default) (stable)

this expression creates a reference which is immediately dereferenced by the compiler
ensure!(!recv.is_empty(), ConnectionClosedSnafu);
reader.consume(bytes_read);
ensure!(bytes_read != 0, ConnectionClosedSnafu);
};
Ok(msg)
}
Expand Down Expand Up @@ -685,7 +689,14 @@
socket.write_all(&buffer).context(WireSendSnafu)?;
buffer.clear();

let msg = get_client_pdu(&mut socket, MAXIMUM_PDU_SIZE, self.strict)?;
// !!!(#589) Soundness issue: if the SCP sends more PDUs in quick succession,
// more data may live in `buf` which may be lost,
// corrupting the PDU reader stream.
let mut buf = BytesMut::with_capacity(MAXIMUM_PDU_SIZE as usize);
let msg = get_client_pdu(&mut socket, &mut buf, MAXIMUM_PDU_SIZE, self.strict)?;
if !buf.is_empty() {
tracing::warn!("Received more data than expected in the first PDU, further issues may arise");
}

match msg {
Pdu::AssociationAC(AssociationAC {
Expand Down