parallel 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/parallel/serializer.rb +52 -0
- data/lib/parallel/version.rb +1 -1
- data/lib/parallel.rb +11 -7
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3331bc6634cd376e4f3f8511049e49a0d4501110aaeac395e2f0c0b638f4a402
|
|
4
|
+
data.tar.gz: 357e1424cb8297b6472c2c5b9486e26da3969edd928be7d6ca7f21670211a8e0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1885d4f814023905f76105f0edce9155024478408be851a4f6869e537400e924e03d34196e03b428803c274b5fbf7e9bf2d93f4548782aa6d3015063c7ed7883
|
|
7
|
+
data.tar.gz: 97d22f6b0320a089a3584e76c0baa245811861776e7337e913961cd64cfe0770131493d7de7e1355a62d6339d47d0b1f3ad32dd89084f31c69a61fcfab9f3148
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'openssl'
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Parallel
|
|
6
|
+
# Pluggable wire serializers. Each must respond to `dump(data, io)` /
|
|
7
|
+
# `load(io)` (used directly by Worker) and `dump(data)` / `load(string)`
|
|
8
|
+
# (used by wrappers like Hmac).
|
|
9
|
+
module Serializer
|
|
10
|
+
# Raw Marshal. Fast but trusts anything written to the pipe — a same-UID
|
|
11
|
+
# attacker that reopens /proc/<pid>/fd/<n> can inject Marshal gadgets (RCE).
|
|
12
|
+
Marshal = ::Marshal
|
|
13
|
+
|
|
14
|
+
# Wraps any inner serializer with a length-prefixed HMAC-SHA256 frame keyed
|
|
15
|
+
# on a per-worker secret generated before fork. Forged frames from a
|
|
16
|
+
# pipe-injector fail verification.
|
|
17
|
+
class Hmac
|
|
18
|
+
LENGTH_FORMAT = 'N' # 32-bit big-endian unsigned int
|
|
19
|
+
LENGTH_BYTES = 4
|
|
20
|
+
MAC_BYTES = 32 # SHA256
|
|
21
|
+
|
|
22
|
+
def initialize(inner: Marshal, secret: SecureRandom.bytes(32))
|
|
23
|
+
@inner = inner
|
|
24
|
+
@secret = secret
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def dump(data, io)
|
|
28
|
+
payload = @inner.dump(data)
|
|
29
|
+
mac = OpenSSL::HMAC.digest('SHA256', @secret, payload)
|
|
30
|
+
io.write([payload.bytesize].pack(LENGTH_FORMAT), mac, payload)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def load(io)
|
|
34
|
+
# nil at frame boundary = clean EOF (worker died / pipe closed between messages)
|
|
35
|
+
header = io.read(LENGTH_BYTES) || raise(EOFError) # eof stops worker
|
|
36
|
+
raise SecurityError, "truncated frame header" if header.bytesize != LENGTH_BYTES
|
|
37
|
+
|
|
38
|
+
length = header.unpack1(LENGTH_FORMAT)
|
|
39
|
+
mac = io.read(MAC_BYTES)
|
|
40
|
+
raise SecurityError, "truncated frame mac" if mac.nil? || mac.bytesize != MAC_BYTES
|
|
41
|
+
|
|
42
|
+
payload = io.read(length)
|
|
43
|
+
raise SecurityError, "truncated frame payload" if payload.nil? || payload.bytesize != length
|
|
44
|
+
|
|
45
|
+
expected = OpenSSL::HMAC.digest('SHA256', @secret, payload)
|
|
46
|
+
raise SecurityError, "HMAC mismatch on worker pipe" unless OpenSSL.fixed_length_secure_compare(mac, expected)
|
|
47
|
+
|
|
48
|
+
@inner.load(payload)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/parallel/version.rb
CHANGED
data/lib/parallel.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require 'rbconfig'
|
|
3
3
|
require 'parallel/version'
|
|
4
|
+
require 'parallel/serializer'
|
|
4
5
|
|
|
5
6
|
module Parallel
|
|
6
7
|
Stop = Object.new.freeze
|
|
@@ -63,10 +64,11 @@ module Parallel
|
|
|
63
64
|
attr_reader :pid, :read, :write
|
|
64
65
|
attr_accessor :thread
|
|
65
66
|
|
|
66
|
-
def initialize(read, write, pid)
|
|
67
|
+
def initialize(read, write, pid, serializer)
|
|
67
68
|
@read = read
|
|
68
69
|
@write = write
|
|
69
70
|
@pid = pid
|
|
71
|
+
@serializer = serializer
|
|
70
72
|
end
|
|
71
73
|
|
|
72
74
|
def stop
|
|
@@ -83,13 +85,13 @@ module Parallel
|
|
|
83
85
|
|
|
84
86
|
def work(data)
|
|
85
87
|
begin
|
|
86
|
-
|
|
88
|
+
@serializer.dump(data, write)
|
|
87
89
|
rescue Errno::EPIPE
|
|
88
90
|
raise DeadWorker
|
|
89
91
|
end
|
|
90
92
|
|
|
91
93
|
result = begin
|
|
92
|
-
|
|
94
|
+
@serializer.load(read)
|
|
93
95
|
rescue EOFError
|
|
94
96
|
raise DeadWorker
|
|
95
97
|
end
|
|
@@ -266,7 +268,7 @@ module Parallel
|
|
|
266
268
|
|
|
267
269
|
if options[:in_processes] && options[:in_threads]
|
|
268
270
|
raise ArgumentError, "Please specify only one of `in_processes` or `in_threads`."
|
|
269
|
-
elsif RUBY_PLATFORM
|
|
271
|
+
elsif RUBY_PLATFORM.include?('java') && !options[:in_processes]
|
|
270
272
|
method = :in_threads
|
|
271
273
|
size = options[method] || processor_count
|
|
272
274
|
elsif options[:in_threads]
|
|
@@ -622,6 +624,7 @@ module Parallel
|
|
|
622
624
|
def worker(job_factory, options, &block)
|
|
623
625
|
child_read, parent_write = IO.pipe
|
|
624
626
|
parent_read, child_write = IO.pipe
|
|
627
|
+
options[:serializer] ||= Serializer::Marshal
|
|
625
628
|
|
|
626
629
|
pid = Process.fork do
|
|
627
630
|
self.worker_number = options[:worker_number]
|
|
@@ -642,12 +645,13 @@ module Parallel
|
|
|
642
645
|
child_read.close
|
|
643
646
|
child_write.close
|
|
644
647
|
|
|
645
|
-
Worker.new(parent_read, parent_write, pid)
|
|
648
|
+
Worker.new(parent_read, parent_write, pid, options[:serializer])
|
|
646
649
|
end
|
|
647
650
|
|
|
648
651
|
def process_incoming_jobs(read, write, job_factory, options, &block)
|
|
652
|
+
serializer = options.fetch(:serializer)
|
|
649
653
|
until read.eof?
|
|
650
|
-
data =
|
|
654
|
+
data = serializer.load(read)
|
|
651
655
|
item, index = job_factory.unpack(data)
|
|
652
656
|
|
|
653
657
|
result =
|
|
@@ -661,7 +665,7 @@ module Parallel
|
|
|
661
665
|
end
|
|
662
666
|
|
|
663
667
|
begin
|
|
664
|
-
|
|
668
|
+
serializer.dump(result, write)
|
|
665
669
|
rescue Errno::EPIPE
|
|
666
670
|
return # parent thread already dead
|
|
667
671
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: parallel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Grosser
|
|
@@ -16,16 +16,17 @@ extra_rdoc_files: []
|
|
|
16
16
|
files:
|
|
17
17
|
- MIT-LICENSE.txt
|
|
18
18
|
- lib/parallel.rb
|
|
19
|
+
- lib/parallel/serializer.rb
|
|
19
20
|
- lib/parallel/version.rb
|
|
20
21
|
homepage: https://github.com/grosser/parallel
|
|
21
22
|
licenses:
|
|
22
23
|
- MIT
|
|
23
24
|
metadata:
|
|
24
25
|
bug_tracker_uri: https://github.com/grosser/parallel/issues
|
|
25
|
-
documentation_uri: https://github.com/grosser/parallel/blob/v2.0
|
|
26
|
-
source_code_uri: https://github.com/grosser/parallel/tree/v2.0
|
|
26
|
+
documentation_uri: https://github.com/grosser/parallel/blob/v2.1.0/Readme.md
|
|
27
|
+
source_code_uri: https://github.com/grosser/parallel/tree/v2.1.0
|
|
27
28
|
wiki_uri: https://github.com/grosser/parallel/wiki
|
|
28
|
-
changelog_uri: https://github.com/grosser/parallel/blob/v2.0
|
|
29
|
+
changelog_uri: https://github.com/grosser/parallel/blob/v2.1.0/CHANGELOG.md
|
|
29
30
|
rubygems_mfa_required: 'true'
|
|
30
31
|
rdoc_options: []
|
|
31
32
|
require_paths:
|