Issues (243)

Security Analysis    2 potential vulnerabilities

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection (2)
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/cli/Threads.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php /** MicroThreads */
2
3
namespace Micro\Cli;
4
5
use Micro\Base\Exception;
6
7
/**
8
 * Threads class file.
9
 *
10
 * @author Oleg Lunegov <[email protected]>
11
 * @link https://github.com/linpax/microphp-framework
12
 * @copyright Copyright (c) 2013 Oleg Lunegov
13
 * @license https://github.com/linpax/microphp-framework/blob/master/LICENSE
14
 * @package Micro
15
 * @subpackage Cli
16
 * @version 1.0
17
 * @since 1.0
18
 */
19
abstract class Threads
20
{
21
    /** @var string $name thread name */
22
    private $name;
23
    /** @var integer $pid process ID */
24
    private $pid;
25
    /** @var integer $puid process UID */
26
    private $puid;
27
    /** @var integer $guid process GUID */
28
    private $guid;
29
    /** @var bool $isChild is child */
30
    private $isChild = false;
31
    /** @var array $internalIPCArray Internal IPC array */
32
    private $internalIPCArray = [];
33
    /** @var integer $internalIPCKey Internal IPC key */
34
    private $internalIPCKey;
35
    /** @var integer $internalSemaphoreKey Internal semaphore key */
36
    private $internalSemaphoreKey;
37
    /** @var bool $isIPC is IPC */
38
    private $isIPC = false;
39
    /** @var bool $running is running */
40
    private $running = false;
41
    /** @var string $fileIPC1 file IPC1 */
42
    private $fileIPC1;
43
    /** @var string $fileIPC2 file IPC2 */
44
    private $fileIPC2;
45
46
47
    /**
48
     * Constructor thread
49
     *
50
     * @access public
51
     *
52
     * @param string $name
53
     * @param int $puid
54
     * @param int $guid
55
     * @param int $umask
56
     *
57
     * @result void
58
     * @throws Exception
59
     */
60
    public function __construct($name, $puid = 0, $guid = 0, $umask = -1)
0 ignored issues
show
__construct uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
61
    {
62
        if (empty($_SERVER['argc'])) {
63
            throw new Exception('Threads are permitted only for CLI');
64
        }
65
66
        $this->name = $name;
67
        $this->guid = $guid;
68
        $this->puid = $puid;
69
70
        if ($umask !== -1) {
71
            umask($umask);
72
        }
73
74
        $this->isChild = false;
75
        $this->internalIPCArray = [];
76
        $this->isIPC = false;
77
78
        if ($this->createIPCSegment() && $this->createIPCSemaphore()) {
79
            $this->isIPC = true;
80
        }
81
    }
82
83
    /**
84
     * Create IPC segment
85
     *
86
     * @access protected
87
     *
88
     * @return bool
89
     * @throws Exception
90
     */
91
    protected function createIPCSegment()
92
    {
93
        $this->fileIPC1 = '/tmp/'.mt_rand().md5($this->getName()).'.shm';
94
95
        touch($this->fileIPC1);
96
97
        $shm_key = ftok($this->fileIPC1, 't');
98
        if ($shm_key === -1) {
99
            throw new Exception('Fatal exception creating SHM segment (ftok)');
100
        }
101
102
        $this->internalIPCKey = shmop_open($shm_key, 'c', 0644, 10240);
103
        if (!$this->internalIPCKey) {
104
            return false;
105
        }
106
107
        return true;
108
    }
109
110
    /**
111
     * get thread name
112
     *
113
     * @access public
114
     * @return string
115
     */
116
    public function getName()
117
    {
118
        return $this->name;
119
    }
120
121
    /**
122
     * Set thread name
123
     *
124
     * @access public
125
     *
126
     * @param string $name
127
     *
128
     * @return void
129
     */
130
    public function setName($name)
131
    {
132
        $this->name = $name;
133
    }
134
135
    /**
136
     * Create IPC semaphore
137
     *
138
     * @access protected
139
     *
140
     * @return bool
141
     * @throws Exception
142
     */
143
    protected function createIPCSemaphore()
144
    {
145
        $this->fileIPC2 = '/tmp/'.mt_rand().md5($this->getName()).'.sem';
146
147
        touch($this->fileIPC2);
148
149
        $sem_key = ftok($this->fileIPC2, 't');
150
        if ($sem_key === -1) {
151
            throw new Exception('Fatal exception creating semaphore (ftok)');
152
        }
153
154
        $this->internalSemaphoreKey = shmop_open($sem_key, 'c', 0644, 10);
155
        if (!$this->internalSemaphoreKey) {
156
            return false;
157
        }
158
159
        return true;
160
    }
161
162
    /**
163
     * Is running thread
164
     *
165
     * @access public
166
     * @return bool
167
     */
168
    public function isRunning()
169
    {
170
        return (bool)$this->running;
171
    }
172
173
    /**
174
     * Set alive
175
     *
176
     * @access public
177
     *
178
     * @return void
179
     * @throws \Micro\Base\Exception
180
     */
181
    public function setAlive()
182
    {
183
        $this->setVariable('_pingTime', time());
184
    }
185
186
    /**
187
     * Set variable in shared memory
188
     *
189
     * @access public
190
     *
191
     * @param string $name
192
     * @param integer $value
193
     *
194
     * @return void
195
     * @throws \Micro\Base\Exception
196
     */
197
    public function setVariable($name, $value)
198
    {
199
        $this->internalIPCArray[$name] = $value;
200
        $this->writeToIPCSegment();
201
    }
202
203
    /**
204
     * Write to IPC segment
205
     *
206
     * @access protected
207
     * @return void
208
     * @throws Exception
209
     */
210
    protected function writeToIPCSegment()
211
    {
212
        if (shmop_read($this->internalSemaphoreKey, 1, 1) === 1) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of shmop_read($this->internalSemaphoreKey, 1, 1) (string) and 1 (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
213
            return;
214
        }
215
216
        $serialized_IPC_array = serialize($this->internalIPCArray);
217
        $shm_bytes_written = shmop_write($this->internalIPCKey, $serialized_IPC_array, 0);
218
219
        if ($shm_bytes_written !== strlen($serialized_IPC_array)) {
220
            throw new Exception(
221
                'Fatal exception writing SHM segment (shmop_write)'.strlen($serialized_IPC_array).
222
                '-'.shmop_size($this->internalIPCKey)
223
            );
224
        }
225
    }
226
227
    /**
228
     * Get last alive
229
     *
230
     * @access public
231
     * @return int
232
     * @throws \Micro\Base\Exception
233
     */
234
    public function getLastAlive()
235
    {
236
        $timestamp = (int)$this->getVariable('_pingTime');
237
        if ($timestamp === 0) {
238
            return 0;
239
        } else {
240
            return (time() - $timestamp);
241
        }
242
    }
243
244
    /**
245
     * Get variable from shared memory
246
     *
247
     * @access public
248
     *
249
     * @param string $name
250
     *
251
     * @return mixed
252
     * @throws \Micro\Base\Exception
253
     */
254
    public function getVariable($name)
255
    {
256
        $this->readFromIPCSegment();
257
258
        return $this->internalIPCArray[$name];
259
    }
260
261
    /**
262
     * Read from IPC segment
263
     *
264
     * @access public
265
     * @return void
266
     * @throws Exception
267
     */
268
    protected function readFromIPCSegment()
269
    {
270
        $serialized_IPC_array = shmop_read($this->internalIPCKey, 0, shmop_size($this->internalIPCKey));
271
272
        if (!$serialized_IPC_array) {
273
            throw new Exception('Fatal exception reading SHM segment (shmop_read)'."\n");
274
        }
275
276
        unset($this->internalIPCArray);
277
278
        $this->internalIPCArray = @unserialize($serialized_IPC_array);
279
    }
280
281
    /**
282
     * Get thread process ID
283
     *
284
     * @access public
285
     * @return int
286
     */
287
    public function getPid()
288
    {
289
        return $this->pid;
290
    }
291
292
    /**
293
     * Register callback func into shared memory
294
     *
295
     * @access public
296
     *
297
     * @param mixed $argList
298
     * @param string $methodName
299
     *
300
     * @return mixed|void
301
     * @throws \Micro\Base\Exception
302
     */
303
    public function register_callback_func($argList, $methodName)
304
    {
305
        if (is_array($argList) && count($argList) > 1) {
306
            if ($argList[1] === -2) {
307
                $this->internalIPCArray['_call_type'] = -2;
308
            } else {
309
                $this->internalIPCArray['_call_type'] = -1;
310
            }
311
        } else {
312
            $this->internalIPCArray['_call_type'] = -1;
313
        }
314
315
        $this->internalIPCArray['_call_method'] = $methodName;
316
        $this->internalIPCArray['_call_input'] = $argList;
317
318
        $this->writeToIPCSegment();
319
320
        switch ($this->internalIPCArray['_call_type']) {
321
            case -1:
322
                $this->sendSigUsr1();
323
                break;
324
325
            case -2:
326
                shmop_write($this->internalSemaphoreKey, 1, 0);
327
328
                $this->sendSigUsr1();
329
                $this->waitIPCSemaphore();
330
                $this->readFromIPCSegment();
331
332
                shmop_write($this->internalSemaphoreKey, 0, 1);
333
334
                return $this->internalIPCArray['_call_output'];
335
        }
336
337
        return false;
338
    }
339
340
    /**
341
     * Send signal USR1
342
     *
343
     * @access protected
344
     * @return void
345
     */
346
    protected function sendSigUsr1()
347
    {
348
        if ($this->pid > 0) {
349
            posix_kill($this->pid, SIGUSR1);
350
        }
351
    }
352
353
    /**
354
     * Wait IPC semaphore
355
     *
356
     * @access protected
357
     * @return void
358
     */
359
    protected function waitIPCSemaphore()
360
    {
361
        while (true) {
362
            $ok = shmop_read($this->internalSemaphoreKey, 0, 1);
363
364
            if ($ok === 0) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $ok (string) and 0 (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
365
                break;
366
            } else {
367
                usleep(10);
368
            }
369
        }
370
    }
371
372
    /**
373
     * Start
374
     *
375
     * @access public
376
     *
377
     * @return void
378
     * @throws Exception
379
     */
380
    public function start()
381
    {
382
        if (!$this->isIPC) {
383
            throw new Exception('Fatal error, unable to create SHM segments for process communications');
384
        }
385
386
        pcntl_signal(SIGCHLD, SIG_IGN);
387
388
        $pid = pcntl_fork();
389
        if ($pid === 0) {
390
            $this->isChild = true;
391
            sleep(1);
392
393
            pcntl_signal(SIGUSR1, [$this, 'sigHandler']);
394
395
            if ($this->guid !== 0) {
396
                posix_setgid($this->guid);
397
            }
398
            if ($this->puid !== 0) {
399
                posix_setuid($this->puid);
400
            }
401
            $this->run();
402
403
            exit(0);
0 ignored issues
show
Coding Style Compatibility introduced by
The method start() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
404
        } else {
405
            $this->isChild = false;
406
            $this->running = true;
407
            $this->pid = $pid;
408
        }
409
    }
410
411
    /**
412
     * Running thread
413
     *
414
     * @access public
415
     * @return void
416
     */
417
    abstract public function run();
418
419
    /**
420
     * Stop thread
421
     *
422
     * @access public
423
     * @return bool
424
     */
425
    public function stop()
426
    {
427
        $success = false;
428
429
        if ($this->pid > 0) {
430
            posix_kill($this->pid, 9);
431
            pcntl_waitpid($this->pid, $temp = 0, WNOHANG);
0 ignored issues
show
$temp = 0 cannot be passed to pcntl_waitpid() as the parameter $status expects a reference.
Loading history...
432
433
            $success = pcntl_wifexited($temp);
434
435
            $this->cleanThreadContext();
436
        }
437
438
        return $success;
439
    }
440
441
    /**
442
     * Clean thread context
443
     *
444
     * @access protected
445
     * @return void
446
     */
447
    protected function cleanThreadContext()
448
    {
449
        shmop_delete($this->internalIPCKey);
450
        shmop_delete($this->internalSemaphoreKey);
451
452
        shmop_close($this->internalIPCKey);
453
        shmop_close($this->internalSemaphoreKey);
454
455
        unlink($this->fileIPC1);
456
        unlink($this->fileIPC2);
457
458
        $this->running = false;
459
        unset($this->pid);
460
    }
461
462
    /**
463
     * Signal handler
464
     *
465
     * @access protected
466
     *
467
     * @param int $sigNo
468
     *
469
     * @return void
470
     * @throws \Micro\Base\Exception
471
     */
472
    protected function sigHandler($sigNo)
473
    {
474
        switch ($sigNo) {
475
            case SIGTERM:
476
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method sigHandler() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
477
478
            case SIGHUP:
479
                break;
480
481
            case SIGUSR1:
482
                $this->readFromIPCSegment();
483
484
                $method = $this->internalIPCArray['_call_method'];
485
                $params = $this->internalIPCArray['_call_input'];
486
487
                switch ($this->internalIPCArray['_call_type']) {
488
                    case -1:
489
                        $this->$method($params);
490
                        break;
491
492
                    case -2:
493
                        $this->internalIPCArray['_call_output'] = $this->$method($params);
494
495
                        $this->writeToIPCSegment();
496
497
                        shmop_write($this->internalSemaphoreKey, 0, 0);
498
                        shmop_write($this->internalSemaphoreKey, 1, 1);
499
500
                        break;
501
                }
502
                break;
503
504
            default:
505
                // handle all other signals
506
        }
507
    }
508
}
509