Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sal/osl/unx/pipe.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <o3tl/safeint.hxx>
21
#include <osl/pipe.h>
22
#include <osl/diagnose.h>
23
#include <osl/thread.h>
24
#include <osl/interlck.h>
25
#include <rtl/string.h>
26
#include <rtl/ustring.h>
27
#include <rtl/bootstrap.hxx>
28
#include <sal/log.hxx>
29
30
#include "sockimpl.hxx"
31
#include "secimpl.hxx"
32
#include "file_impl.hxx"
33
#include "unixerrnostring.hxx"
34
35
#include <cassert>
36
#include <cstring>
37
#include <fcntl.h>
38
#include <sys/stat.h>
39
#include <unistd.h>
40
41
constexpr OString PIPEDEFAULTPATH = "/tmp"_ostr;
42
constexpr OString PIPEALTERNATEPATH = "/var/tmp"_ostr;
43
44
static oslPipe osl_psz_createPipe(const char *pszPipeName, oslPipeOptions Options, oslSecurity Security);
45
46
struct
47
{
48
    int            errcode;
49
    oslPipeError   error;
50
} const PipeError[]= {
51
    { 0,               osl_Pipe_E_None              },  /* no error */
52
    { EPROTOTYPE,      osl_Pipe_E_NoProtocol        },  /* Protocol wrong type for socket */
53
    { ENOPROTOOPT,     osl_Pipe_E_NoProtocol        },  /* Protocol not available */
54
    { EPROTONOSUPPORT, osl_Pipe_E_NoProtocol        },  /* Protocol not supported */
55
#ifdef ESOCKTNOSUPPORT
56
    { ESOCKTNOSUPPORT, osl_Pipe_E_NoProtocol        },  /* Socket type not supported */
57
#endif
58
    { EPFNOSUPPORT,    osl_Pipe_E_NoProtocol        },  /* Protocol family not supported */
59
    { EAFNOSUPPORT,    osl_Pipe_E_NoProtocol        },  /* Address family not supported by */
60
                                                        /* protocol family */
61
    { ENETRESET,       osl_Pipe_E_NetworkReset      },  /* Network dropped connection because */
62
                                                         /* of reset */
63
    { ECONNABORTED,    osl_Pipe_E_ConnectionAbort   },  /* Software caused connection abort */
64
    { ECONNRESET,      osl_Pipe_E_ConnectionReset   },  /* Connection reset by peer */
65
    { ENOBUFS,         osl_Pipe_E_NoBufferSpace     },  /* No buffer space available */
66
    { ETIMEDOUT,       osl_Pipe_E_TimedOut          },  /* Connection timed out */
67
    { ECONNREFUSED,    osl_Pipe_E_ConnectionRefused },  /* Connection refused */
68
    { -1,              osl_Pipe_E_invalidError      }
69
};
70
71
static oslPipeError osl_PipeErrorFromNative(int nativeType)
72
0
{
73
0
    int i = 0;
74
75
0
    while ((PipeError[i].error != osl_Pipe_E_invalidError) &&
76
0
           (PipeError[i].errcode != nativeType))
77
0
    {
78
0
        i++;
79
0
    }
80
81
0
    return PipeError[i].error;
82
0
}
83
84
static oslPipe createPipeImpl()
85
0
{
86
0
    oslPipe pPipeImpl = new oslPipeImpl;
87
88
0
    pPipeImpl->m_Socket = 0;
89
0
    pPipeImpl->m_Name[0] = 0;
90
0
    pPipeImpl->m_nRefCount = 1;
91
0
    pPipeImpl->m_bClosed = false;
92
0
#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
93
0
    pPipeImpl->m_bIsInShutdown = false;
94
0
    pPipeImpl->m_bIsAccepting = false;
95
0
#endif
96
97
0
    return pPipeImpl;
98
0
}
99
100
static void destroyPipeImpl(oslPipe pImpl)
101
0
{
102
0
    delete pImpl;
103
0
}
104
105
oslPipe SAL_CALL osl_createPipe(rtl_uString *ustrPipeName, oslPipeOptions Options, oslSecurity Security)
106
0
{
107
0
    oslPipe pPipe = nullptr;
108
0
    rtl_String* strPipeName = nullptr;
109
110
0
    if (ustrPipeName)
111
0
    {
112
0
        rtl_uString2String(&strPipeName,
113
0
                           rtl_uString_getStr(ustrPipeName),
114
0
                           rtl_uString_getLength(ustrPipeName),
115
0
                           osl_getThreadTextEncoding(),
116
0
                           OUSTRING_TO_OSTRING_CVTFLAGS);
117
0
        char* pszPipeName = rtl_string_getStr(strPipeName);
118
0
        pPipe = osl_psz_createPipe(pszPipeName, Options, Security);
119
120
0
        if (strPipeName)
121
0
            rtl_string_release(strPipeName);
122
0
    }
123
124
0
    return pPipe;
125
126
0
}
127
128
static OString
129
getBootstrapSocketPath()
130
0
{
131
0
    OUString pValue;
132
133
0
    if (rtl::Bootstrap::get(u"OSL_SOCKET_PATH"_ustr, pValue))
134
0
    {
135
0
        return OUStringToOString(pValue, RTL_TEXTENCODING_UTF8);
136
0
    }
137
0
    return ""_ostr;
138
0
}
139
140
static oslPipe osl_psz_createPipe(const char *pszPipeName, oslPipeOptions Options,
141
                                    oslSecurity Security)
142
0
{
143
0
    int Flags;
144
0
    size_t len;
145
0
    struct sockaddr_un addr;
146
147
0
    OString name;
148
0
    oslPipe pPipe;
149
150
0
    if (access(PIPEDEFAULTPATH.getStr(), W_OK) == 0)
151
0
        name = PIPEDEFAULTPATH;
152
0
    else if (access(PIPEALTERNATEPATH.getStr(), W_OK) == 0)
153
0
        name = PIPEALTERNATEPATH;
154
0
    else {
155
0
        name = getBootstrapSocketPath ();
156
0
    }
157
158
0
    name += "/";
159
160
0
    if (Security)
161
0
    {
162
0
        char Ident[256];
163
164
0
        Ident[0] = '\0';
165
166
0
        OSL_VERIFY(osl_psz_getUserIdent(Security, Ident, sizeof(Ident)));
167
168
0
        name += OString::Concat("OSL_PIPE_") + Ident + "_" + pszPipeName;
169
0
    }
170
0
    else
171
0
    {
172
0
        name += OString::Concat("OSL_PIPE_") + pszPipeName;
173
0
    }
174
175
0
    if (o3tl::make_unsigned(name.getLength()) >= sizeof addr.sun_path)
176
0
    {
177
0
        SAL_WARN("sal.osl.pipe", "osl_createPipe: pipe name too long");
178
0
        return nullptr;
179
0
    }
180
181
    /* alloc memory */
182
0
    pPipe = createPipeImpl();
183
184
0
    if (!pPipe)
185
0
        return nullptr;
186
187
    /* create socket */
188
0
    pPipe->m_Socket = socket(AF_UNIX, SOCK_STREAM, 0);
189
0
    if (pPipe->m_Socket < 0)
190
0
    {
191
0
        SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno));
192
0
        destroyPipeImpl(pPipe);
193
0
        return nullptr;
194
0
    }
195
196
    /* set close-on-exec flag */
197
0
    if ((Flags = fcntl(pPipe->m_Socket, F_GETFD, 0)) != -1)
198
0
    {
199
0
        Flags |= FD_CLOEXEC;
200
0
        if (fcntl(pPipe->m_Socket, F_SETFD, Flags) == -1)
201
0
        {
202
0
            SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno));
203
0
        }
204
0
    }
205
206
0
    memset(&addr, 0, sizeof(addr));
207
208
0
    SAL_INFO("sal.osl.pipe", "new pipe on fd " << pPipe->m_Socket << " '" << name << "'");
209
210
0
    if (isForbidden(name, osl_File_OpenFlag_Create))
211
0
    {
212
0
        close (pPipe->m_Socket);
213
0
        destroyPipeImpl(pPipe);
214
0
        return nullptr;
215
0
    }
216
217
0
    addr.sun_family = AF_UNIX;
218
0
    strcpy(addr.sun_path, name.getStr());
219
#if defined(FREEBSD)
220
    len = SUN_LEN(&addr);
221
#else
222
0
    len = sizeof(addr);
223
0
#endif
224
225
0
    if (Options & osl_Pipe_CREATE)
226
0
    {
227
0
        struct stat status;
228
229
        /* check if there exists an orphan filesystem entry */
230
0
        if ((stat(name.getStr(), &status) == 0) &&
231
0
            (S_ISSOCK(status.st_mode) || S_ISFIFO(status.st_mode)))
232
0
        {
233
0
            if (connect(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) >= 0)
234
0
            {
235
0
                close (pPipe->m_Socket);
236
0
                destroyPipeImpl(pPipe);
237
0
                return nullptr;
238
0
            }
239
240
0
            unlink(name.getStr());
241
0
        }
242
243
        /* ok, fs clean */
244
0
        if (bind(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) < 0)
245
0
        {
246
0
            SAL_WARN("sal.osl.pipe", "bind() failed: " << UnixErrnoString(errno));
247
0
            close(pPipe->m_Socket);
248
0
            destroyPipeImpl(pPipe);
249
0
            return nullptr;
250
0
        }
251
252
        /*  Only give access to all if no security handle was specified, otherwise security
253
            depends on umask */
254
255
0
        if (!Security)
256
0
            (void)chmod(name.getStr(),S_IRWXU | S_IRWXG |S_IRWXO);
257
258
0
        strcpy(pPipe->m_Name, name.getStr()); // safe, see check above
259
260
0
        if (listen(pPipe->m_Socket, 5) < 0)
261
0
        {
262
0
            SAL_WARN("sal.osl.pipe", "listen() failed: " << UnixErrnoString(errno));
263
0
            unlink(name.getStr());   /* remove filesystem entry */
264
0
            close(pPipe->m_Socket);
265
0
            destroyPipeImpl(pPipe);
266
0
            return nullptr;
267
0
        }
268
269
0
        return pPipe;
270
0
    }
271
272
    /* osl_pipe_OPEN */
273
0
    if (access(name.getStr(), F_OK) != -1)
274
0
    {
275
0
        if (connect(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) >= 0)
276
0
            return pPipe;
277
278
0
        SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
279
0
    }
280
281
0
    close (pPipe->m_Socket);
282
0
    destroyPipeImpl(pPipe);
283
0
    return nullptr;
284
0
}
285
286
void SAL_CALL osl_acquirePipe(oslPipe pPipe)
287
0
{
288
0
    osl_atomic_increment(&(pPipe->m_nRefCount));
289
0
}
290
291
void SAL_CALL osl_releasePipe(oslPipe pPipe)
292
0
{
293
0
    if (!pPipe)
294
0
        return;
295
296
0
    if (osl_atomic_decrement(&(pPipe->m_nRefCount)) == 0)
297
0
    {
298
0
        osl_closePipe(pPipe);
299
300
0
        destroyPipeImpl(pPipe);
301
0
    }
302
0
}
303
304
void SAL_CALL osl_closePipe(oslPipe pPipe)
305
0
{
306
0
    int nRet;
307
0
    int ConnFD;
308
309
0
    if (!pPipe)
310
0
        return;
311
312
0
    std::unique_lock aGuard(pPipe->m_Mutex);
313
314
0
    if (pPipe->m_bClosed)
315
0
        return;
316
317
0
    ConnFD = pPipe->m_Socket;
318
319
    /* Thread does not return from accept on linux, so
320
       connect to the accepting pipe
321
     */
322
0
#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
323
0
    struct sockaddr_un addr;
324
325
0
    if (pPipe->m_bIsAccepting)
326
0
    {
327
0
        pPipe->m_bIsInShutdown = true;
328
0
        pPipe->m_Socket = -1;
329
330
0
        int fd = socket(AF_UNIX, SOCK_STREAM, 0);
331
0
        if (fd < 0)
332
0
        {
333
0
            SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno));
334
0
            return;
335
0
        }
336
337
0
        memset(&addr, 0, sizeof(addr));
338
339
0
        SAL_INFO("sal.osl.pipe", "osl_destroyPipe : Pipe Name '" << pPipe->m_Name << "'");
340
341
0
        addr.sun_family = AF_UNIX;
342
0
        strcpy(addr.sun_path, pPipe->m_Name); // safe, as both are same size
343
344
0
        nRet = connect(fd, reinterpret_cast< sockaddr* >(&addr), sizeof(addr));
345
0
        if (nRet < 0)
346
0
            SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
347
348
0
        close(fd);
349
0
    }
350
0
#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
351
352
0
    nRet = shutdown(ConnFD, 2);
353
0
    if (nRet < 0)
354
0
        SAL_WARN("sal.osl.pipe", "shutdown() failed: " << UnixErrnoString(errno));
355
356
0
    nRet = close(ConnFD);
357
0
    if (nRet < 0)
358
0
        SAL_WARN("sal.osl.pipe", "close() failed: " << UnixErrnoString(errno));
359
360
    /* remove filesystem entry */
361
0
    if (pPipe->m_Name[0] != '\0')
362
0
        unlink(pPipe->m_Name);
363
364
0
    pPipe->m_bClosed = true;
365
0
}
366
367
oslPipe SAL_CALL osl_acceptPipe(oslPipe pPipe)
368
0
{
369
0
    int s;
370
0
    oslPipe pAcceptedPipe;
371
372
0
    SAL_WARN_IF(!pPipe, "sal.osl.pipe", "invalid pipe");
373
0
    if (!pPipe)
374
0
        return nullptr;
375
376
0
    int socket;
377
0
    {
378
        // don't hold lock while accepting, so it is possible to close a socket blocked in accept
379
0
        std::unique_lock aGuard(pPipe->m_Mutex);
380
381
0
        assert(pPipe->m_Name[0] != '\0');  // you cannot have an empty pipe name
382
0
#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
383
0
        pPipe->m_bIsAccepting = true;
384
0
#endif
385
386
0
        socket = pPipe->m_Socket;
387
0
    }
388
389
390
0
    s = accept(socket, nullptr, nullptr);
391
392
0
    std::unique_lock aGuard(pPipe->m_Mutex);
393
394
0
#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
395
0
    pPipe->m_bIsAccepting = false;
396
0
#endif
397
398
0
    if (s < 0)
399
0
    {
400
0
        SAL_WARN("sal.osl.pipe", "accept() failed: " << UnixErrnoString(errno));
401
0
        return nullptr;
402
0
    }
403
404
0
#if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
405
0
    if (pPipe->m_bIsInShutdown)
406
0
    {
407
0
        close(s);
408
0
        return nullptr;
409
0
    }
410
0
#endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
411
412
    /* alloc memory */
413
0
    pAcceptedPipe = createPipeImpl();
414
415
0
    assert(pAcceptedPipe);  // should never be the case that an oslPipe cannot be initialized
416
0
    if (!pAcceptedPipe)
417
0
    {
418
0
        close(s);
419
0
        return nullptr;
420
0
    }
421
422
    /* set close-on-exec flag */
423
0
    int flags;
424
0
    if ((flags = fcntl(s, F_GETFD, 0)) >= 0)
425
0
    {
426
0
        flags |= FD_CLOEXEC;
427
0
        if (fcntl(s, F_SETFD, flags) < 0)
428
0
            SAL_WARN("sal.osl.pipe", "fcntl() failed: " <<  UnixErrnoString(errno));
429
0
    }
430
431
0
    pAcceptedPipe->m_Socket = s;
432
433
0
    return pAcceptedPipe;
434
0
}
435
436
sal_Int32 SAL_CALL osl_receivePipe(oslPipe pPipe,
437
                        void* pBuffer,
438
                        sal_Int32 BytesToRead)
439
0
{
440
0
    SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_receivePipe: invalid pipe");
441
0
    if (!pPipe)
442
0
    {
443
0
        SAL_WARN("sal.osl.pipe", "osl_receivePipe: Invalid socket");
444
0
        errno=EINVAL;
445
0
        return -1;
446
0
    }
447
448
0
    int socket;
449
0
    {
450
        // don't hold lock while receiving, so it is possible to close a socket blocked in recv
451
0
        std::unique_lock aGuard(pPipe->m_Mutex);
452
0
        socket = pPipe->m_Socket;
453
0
    }
454
455
0
    sal_Int32 nRet = recv(socket, pBuffer, BytesToRead, 0);
456
457
0
    SAL_WARN_IF(nRet < 0, "sal.osl.pipe", "recv() failed: " << UnixErrnoString(errno));
458
459
0
    return nRet;
460
0
}
461
462
sal_Int32 SAL_CALL osl_sendPipe(oslPipe pPipe,
463
                       const void* pBuffer,
464
                       sal_Int32 BytesToSend)
465
0
{
466
0
    int nRet=0;
467
468
0
    SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_sendPipe: invalid pipe");
469
0
    if (!pPipe)
470
0
    {
471
0
        SAL_WARN("sal.osl.pipe", "osl_sendPipe: Invalid socket");
472
0
        errno=EINVAL;
473
0
        return -1;
474
0
    }
475
476
0
    int socket;
477
0
    {
478
        // don't hold lock while sending, so it is possible to close a socket blocked in send
479
0
        std::unique_lock aGuard(pPipe->m_Mutex);
480
0
        socket = pPipe->m_Socket;
481
0
    }
482
483
0
    nRet = send(socket, pBuffer, BytesToSend, 0);
484
485
0
    if (nRet <= 0)
486
0
        SAL_WARN("sal.osl.pipe", "send() failed: " << UnixErrnoString(errno));
487
488
0
    return nRet;
489
0
}
490
491
oslPipeError SAL_CALL osl_getLastPipeError(SAL_UNUSED_PARAMETER oslPipe)
492
0
{
493
0
    return osl_PipeErrorFromNative(errno);
494
0
}
495
496
sal_Int32 SAL_CALL osl_writePipe(oslPipe pPipe, const void *pBuffer, sal_Int32 n)
497
0
{
498
    /* loop until all desired bytes were send or an error occurred */
499
0
    sal_Int32 BytesSend = 0;
500
0
    sal_Int32 BytesToSend = n;
501
502
0
    SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_writePipe: invalid pipe"); // osl_sendPipe detects invalid pipe
503
0
    while (BytesToSend > 0)
504
0
    {
505
0
        sal_Int32 RetVal = osl_sendPipe(pPipe, pBuffer, BytesToSend);
506
        /* error occurred? */
507
0
        if (RetVal <= 0)
508
0
            break;
509
510
0
        BytesToSend -= RetVal;
511
0
        BytesSend += RetVal;
512
0
        pBuffer= static_cast< char const* >(pBuffer) + RetVal;
513
0
    }
514
515
0
    return BytesSend;
516
0
}
517
518
sal_Int32 SAL_CALL osl_readPipe( oslPipe pPipe, void *pBuffer , sal_Int32 n )
519
0
{
520
    /* loop until all desired bytes were read or an error occurred */
521
0
    sal_Int32 BytesRead = 0;
522
0
    sal_Int32 BytesToRead = n;
523
524
0
    SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_readPipe: invalid pipe"); // osl_receivePipe detects invalid pipe
525
0
    while (BytesToRead > 0)
526
0
    {
527
0
        sal_Int32 RetVal = osl_receivePipe(pPipe, pBuffer, BytesToRead);
528
        /* error occurred? */
529
0
        if (RetVal <= 0)
530
0
            break;
531
532
0
        BytesToRead -= RetVal;
533
0
        BytesRead += RetVal;
534
0
        pBuffer= static_cast< char* >(pBuffer) + RetVal;
535
0
    }
536
537
0
    return BytesRead;
538
0
}
539
540
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */