Send SIGPIPE for closed pipes.

Fixes #5974
Updates #161

PiperOrigin-RevId: 375024740
This commit is contained in:
Ian Lewis
2021-05-20 23:44:41 -07:00
committed by gVisor bot
parent 5bbc227971
commit 2bed0bb096
6 changed files with 94 additions and 0 deletions
+2
View File
@@ -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",
],
+36
View File
@@ -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
}
+6
View File
@@ -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
}
+5
View File
@@ -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:
+1
View File
@@ -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",
+44
View File
@@ -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, &notify]() {
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();
}