Commit Diff


commit - 62b68cc461b5e298add3ab190fe9a38f3efefe7a
commit + 290f53c38cdacd502eb6dda52f2ad207063e3973
blob - 860d1c9f93b33a9120cd97b1cb15d07d5a1e3f30
blob + 5c4c473954d38d0fa3522989b5154891ebf285f6
--- src/bin/tp.rs
+++ src/bin/tp.rs
@@ -110,15 +110,28 @@ fn main() {
                     std::process::exit(1);
                 }
             };
+            // Read at most MAX_PASTE + 1 byte so we can detect
+            // oversized input without unbounded allocation.
+            const MAX_PASTE: usize = 64 * 1024;
             let mut content = Vec::new();
-            if let Err(e) = std::io::stdin().read_to_end(&mut content) {
-                eprintln!("error: reading stdin: {e}");
-                std::process::exit(1);
+            match std::io::stdin()
+                .take((MAX_PASTE + 1) as u64)
+                .read_to_end(&mut content)
+            {
+                Ok(0) => {
+                    eprintln!("error: empty input");
+                    std::process::exit(1);
+                }
+                Ok(n) if n > MAX_PASTE => {
+                    eprintln!("error: input exceeds 64 KiB limit");
+                    std::process::exit(1);
+                }
+                Err(e) => {
+                    eprintln!("error: reading stdin: {e}");
+                    std::process::exit(1);
+                }
+                _ => {}
             }
-            if content.is_empty() {
-                eprintln!("error: empty input");
-                std::process::exit(1);
-            }
             let cmd = if public { "PUTP" } else { "PUT" };
             format!("{cmd} {ttl_secs} {}\n", base58::encode(&content))
         }
blob - a1edf799fc96acd3bcab63c318fd51e7b8fed377
blob + e1ebc7b34d8abe1f842c306dbb5307671f5be518
--- src/bin/tpd.rs
+++ src/bin/tpd.rs
@@ -256,7 +256,9 @@ fn main() {
 
     let shutdown = Arc::new(AtomicBool::new(false));
 
-    // Signal handler
+    // Signal handler — Arc::into_raw intentionally leaks the
+    // refcount so the pointer remains valid for the process
+    // lifetime. No matching Arc::from_raw needed.
     let sig = Arc::clone(&shutdown);
     unsafe {
         SHUTDOWN_PTR.store(
blob - f12efd90c7433e2f60e65042de2b2693fe2f56e3
blob + 12757a31f72beda55941bba8bae207bd57e02626
--- src/daemon.rs
+++ src/daemon.rs
@@ -298,9 +298,13 @@ fn handle_client(
                 "request too large".into(),
             ));
             writer.write_all(resp.as_bytes())?;
-            // Drain remaining bytes until newline
-            let mut discard = String::new();
-            let _ = reader.read_line(&mut discard);
+            // Drain remaining bytes until newline (bounded to
+            // prevent a client without newlines from blocking
+            // indefinitely beyond the read timeout).
+            let mut discard = Vec::new();
+            let _ = (&mut reader)
+                .take(MAX_LINE_SIZE as u64)
+                .read_until(b'\n', &mut discard);
             continue;
         }
         let line = line.trim();