Skip to content
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
3 changes: 3 additions & 0 deletions docs/RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ only the filesystem-accessing *functions* are forbidden.
- Commands MUST NOT exhaust file descriptors or other system resources
- Commands MUST handle interruption (context cancellation) gracefully

### Goroutine Context Propagation
- Goroutines spawned during command or interpreter execution MUST propagate and respect context cancellation. Before any blocking I/O in a goroutine, check `ctx.Err()`. For multi-chunk writes, check `ctx.Err()` between chunks. Close the pipe write end (`pw.Close()`) on cancellation to unblock the reader and propagate termination.

### Integer Safety
- Commands MUST check for integer overflow in all arithmetic operations
- Commands MUST validate numeric conversions (string to int) and handle errors
Expand Down
40 changes: 34 additions & 6 deletions interp/runner_redir.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func hdocLiteralPart(buf *strings.Builder, part syntax.WordPart) {
}
}

func (r *Runner) hdocReader(rd *syntax.Redirect) (*os.File, error) {
func (r *Runner) hdocReader(ctx context.Context, rd *syntax.Redirect) (*os.File, error) {
pr, pw, err := os.Pipe()
if err != nil {
return nil, err
Expand All @@ -94,8 +94,22 @@ func (r *Runner) hdocReader(rd *syntax.Redirect) (*os.File, error) {
return nil, fmt.Errorf("heredoc: content exceeds maximum size (%d bytes)", MaxHeredocBytes)
}
go func() {
pw.WriteString(hdoc)
pw.Close()
defer pw.Close()
const chunkSize = 32 * 1024
data := []byte(hdoc)
Comment thread
thieman marked this conversation as resolved.
for len(data) > 0 {
if ctx.Err() != nil {
return
}
n := chunkSize
if n > len(data) {
n = len(data)
}
if _, err := pw.Write(data[:n]); err != nil {
return
Comment thread
thieman marked this conversation as resolved.
}
data = data[n:]
}
}()
return pr, nil
}
Expand Down Expand Up @@ -144,15 +158,29 @@ func (r *Runner) hdocReader(rd *syntax.Redirect) (*os.File, error) {
return nil, hdocErr
}
go func() {
pw.Write(buf.Bytes())
pw.Close()
defer pw.Close()
const chunkSize = 32 * 1024
data := buf.Bytes()
for len(data) > 0 {
if ctx.Err() != nil {
return
}
n := chunkSize
if n > len(data) {
n = len(data)
}
if _, err := pw.Write(data[:n]); err != nil {
return
Comment thread
thieman marked this conversation as resolved.
}
data = data[n:]
}
}()
return pr, nil
}

func (r *Runner) redir(ctx context.Context, rd *syntax.Redirect) (io.Closer, error) {
if rd.Hdoc != nil {
pr, err := r.hdocReader(rd)
pr, err := r.hdocReader(ctx, rd)
if err != nil {
return nil, err
}
Expand Down
Loading