mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Send SIGPIPE for closed pipes.
Fixes #5974 Updates #161 PiperOrigin-RevId: 375024740
This commit is contained in:
@@ -15,6 +15,7 @@ go_library(
|
||||
"bpf.go",
|
||||
"capability.go",
|
||||
"clone.go",
|
||||
"context.go",
|
||||
"dev.go",
|
||||
"elf.go",
|
||||
"epoll.go",
|
||||
@@ -77,6 +78,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/abi",
|
||||
"//pkg/bits",
|
||||
"//pkg/context",
|
||||
"//pkg/marshal",
|
||||
"//pkg/marshal/primitive",
|
||||
],
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package linux
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
)
|
||||
|
||||
// contextID is the linux package's type for context.Context.Value keys.
|
||||
type contextID int
|
||||
|
||||
const (
|
||||
// CtxSignalNoInfoFunc is a Context.Value key for a function to send signals.
|
||||
CtxSignalNoInfoFunc contextID = iota
|
||||
)
|
||||
|
||||
// SignalNoInfoFuncFromContext returns a callback function that can be used to send a
|
||||
// signal to the given context.
|
||||
func SignalNoInfoFuncFromContext(ctx context.Context) func(Signal) error {
|
||||
if f := ctx.Value(CtxSignalNoInfoFunc); f != nil {
|
||||
return f.(func(Signal) error)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -86,6 +86,12 @@ func (p *Pipe) Write(ctx context.Context, src usermem.IOSequence) (int64, error)
|
||||
if n > 0 {
|
||||
p.Notify(waiter.ReadableEvents)
|
||||
}
|
||||
if err == unix.EPIPE {
|
||||
// If we are returning EPIPE send SIGPIPE to the task.
|
||||
if sendSig := linux.SignalNoInfoFuncFromContext(ctx); sendSig != nil {
|
||||
sendSig(linux.SIGPIPE)
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package kernel
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
@@ -113,6 +114,10 @@ func (t *Task) contextValue(key interface{}, isTaskGoroutine bool) interface{} {
|
||||
return t.k.RealtimeClock()
|
||||
case limits.CtxLimits:
|
||||
return t.tg.limits
|
||||
case linux.CtxSignalNoInfoFunc:
|
||||
return func(sig linux.Signal) error {
|
||||
return t.SendSignal(SignalInfoNoInfo(sig, t, t))
|
||||
}
|
||||
case pgalloc.CtxMemoryFile:
|
||||
return t.k.mf
|
||||
case pgalloc.CtxMemoryFileProvider:
|
||||
|
||||
@@ -1575,6 +1575,7 @@ cc_binary(
|
||||
"@com_google_absl//absl/time",
|
||||
gtest,
|
||||
"//test/util:posix_error",
|
||||
"//test/util:signal_util",
|
||||
"//test/util:temp_path",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <fcntl.h> /* Obtain O_* constant definitions */
|
||||
#include <linux/magic.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <sys/uio.h>
|
||||
@@ -29,6 +30,7 @@
|
||||
#include "test/util/file_descriptor.h"
|
||||
#include "test/util/fs_util.h"
|
||||
#include "test/util/posix_error.h"
|
||||
#include "test/util/signal_util.h"
|
||||
#include "test/util/temp_path.h"
|
||||
#include "test/util/test_util.h"
|
||||
#include "test/util/thread_util.h"
|
||||
@@ -44,6 +46,28 @@ constexpr int kTestValue = 0x12345678;
|
||||
// Used for synchronization in race tests.
|
||||
const absl::Duration syncDelay = absl::Seconds(2);
|
||||
|
||||
std::atomic<int> global_num_signals_received = 0;
|
||||
void SigRecordingHandler(int signum, siginfo_t* siginfo,
|
||||
void* unused_ucontext) {
|
||||
global_num_signals_received++;
|
||||
}
|
||||
|
||||
PosixErrorOr<Cleanup> RegisterSignalHandler(int signum) {
|
||||
struct sigaction handler;
|
||||
handler.sa_sigaction = SigRecordingHandler;
|
||||
sigemptyset(&handler.sa_mask);
|
||||
handler.sa_flags = SA_SIGINFO;
|
||||
return ScopedSigaction(signum, handler);
|
||||
}
|
||||
|
||||
void WaitForSignalDelivery(absl::Duration timeout, int max_expected) {
|
||||
absl::Time wait_start = absl::Now();
|
||||
while (global_num_signals_received < max_expected &&
|
||||
absl::Now() - wait_start < timeout) {
|
||||
absl::SleepFor(absl::Milliseconds(10));
|
||||
}
|
||||
}
|
||||
|
||||
struct PipeCreator {
|
||||
std::string name_;
|
||||
|
||||
@@ -333,10 +357,16 @@ TEST_P(PipeTest, WriterSideClosesReadDataFirst) {
|
||||
TEST_P(PipeTest, ReaderSideCloses) {
|
||||
SKIP_IF(!CreateBlocking());
|
||||
|
||||
const auto signal_cleanup =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGPIPE));
|
||||
|
||||
ASSERT_THAT(close(rfd_.release()), SyscallSucceeds());
|
||||
int buf = kTestValue;
|
||||
EXPECT_THAT(write(wfd_.get(), &buf, sizeof(buf)),
|
||||
SyscallFailsWithErrno(EPIPE));
|
||||
|
||||
WaitForSignalDelivery(absl::Seconds(1), 1);
|
||||
ASSERT_EQ(global_num_signals_received, 1);
|
||||
}
|
||||
|
||||
TEST_P(PipeTest, CloseTwice) {
|
||||
@@ -355,6 +385,9 @@ TEST_P(PipeTest, CloseTwice) {
|
||||
TEST_P(PipeTest, BlockWriteClosed) {
|
||||
SKIP_IF(!CreateBlocking());
|
||||
|
||||
const auto signal_cleanup =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGPIPE));
|
||||
|
||||
absl::Notification notify;
|
||||
ScopedThread t([this, ¬ify]() {
|
||||
std::vector<char> buf(Size());
|
||||
@@ -371,6 +404,10 @@ TEST_P(PipeTest, BlockWriteClosed) {
|
||||
|
||||
notify.WaitForNotification();
|
||||
ASSERT_THAT(close(rfd_.release()), SyscallSucceeds());
|
||||
|
||||
WaitForSignalDelivery(absl::Seconds(1), 1);
|
||||
ASSERT_EQ(global_num_signals_received, 1);
|
||||
|
||||
t.Join();
|
||||
}
|
||||
|
||||
@@ -379,6 +416,9 @@ TEST_P(PipeTest, BlockWriteClosed) {
|
||||
TEST_P(PipeTest, BlockPartialWriteClosed) {
|
||||
SKIP_IF(!CreateBlocking());
|
||||
|
||||
const auto signal_cleanup =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGPIPE));
|
||||
|
||||
ScopedThread t([this]() {
|
||||
const int pipe_size = Size();
|
||||
std::vector<char> buf(2 * pipe_size);
|
||||
@@ -396,6 +436,10 @@ TEST_P(PipeTest, BlockPartialWriteClosed) {
|
||||
|
||||
// Unblock the above.
|
||||
ASSERT_THAT(close(rfd_.release()), SyscallSucceeds());
|
||||
|
||||
WaitForSignalDelivery(absl::Seconds(1), 2);
|
||||
ASSERT_EQ(global_num_signals_received, 2);
|
||||
|
||||
t.Join();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user