rex-core 0.1.35 → 0.1.37

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 722c1d73aa2712e77940801d1f02e44565e0234944e7c50d8d82f6db845bce0f
4
- data.tar.gz: 295ecbe54da2c7b54f6f19b7c8676140fcb2f18dcc660eaedfef7aa3c5633b9a
3
+ metadata.gz: c1b69ef9a2e48921a8f6102041c3ee29b4de5004e74874966827cc6707d0eb13
4
+ data.tar.gz: e6b9f06e4e58e33e448b4119e337bcc36a1a23724655119ffef9dba983c910b1
5
5
  SHA512:
6
- metadata.gz: e2e5510ad24f1770fb127efd2cee8b0b08653be57eb15b7c44e51ef03a1fc6d23c5636259c632544a1d7ed17fa08dca289ee2dc3161c730803ab852a63ec677f
7
- data.tar.gz: a5998d868389e099d9108dcdf9a4f86d0e91b6053f2cc3cb2fea75e84e3f5ddfa86136d018f70cd150817e86d12a455f4bce9ddcf0794f1ab512a64c606c4f40
6
+ metadata.gz: fbda8aea3da6fd99b2a80ffb0164d3af1f156f1b36823ed5e3816bd65583b280e7d51651a5f89d9702cdb5fe8b086d9393e8ece32606b89a7cd2822a6ad34b25
7
+ data.tar.gz: e521512ef7be86e68d46afb39497c2d128033a5642140327a7d723b391dc848a40579112c2c2a7d8acea12ab8a7b8f7a5bf79510f489f50094c1725588adaa37
@@ -1,5 +1,5 @@
1
1
  module Rex
2
2
  module Core
3
- VERSION = "0.1.35"
3
+ VERSION = "0.1.37"
4
4
  end
5
5
  end
@@ -15,29 +15,25 @@ module IO
15
15
  @ready = []
16
16
  @pending = []
17
17
  @blocking = 0
18
- @urgent = Rex::Compat.pipe
18
+ @urgent = nil
19
19
  @mutex = Mutex.new
20
20
  end
21
21
 
22
22
  # This allows the fiber to be scheduled in the #run thread from another thread
23
23
  def schedule_fiber(&block)
24
24
  @mutex.synchronize do
25
+ @urgent ||= Rex::Compat.pipe
25
26
  @pending << block
27
+ # Wake up the scheduler while holding the same lock used by #reset! so
28
+ # the signaling pipe cannot be replaced between lookup and write.
29
+ @urgent.last.write_nonblock('.', exception: false)
26
30
  end
27
- # Wake up the scheduler
28
- @urgent.last.write_nonblock('.') rescue nil
29
31
  end
30
32
 
31
33
  def run
32
34
  while @readable.any? or @writable.any? or @waiting.any? or @blocking.positive? or @ready.any? or @pending.any?
33
35
  # Start any pending fibers
34
- pending_blocks = []
35
- @mutex.synchronize do
36
- pending_blocks = @pending.dup
37
- @pending.clear
38
- end
39
-
40
- pending_blocks.each do |block|
36
+ while (block = @mutex.synchronize { @pending.shift })
41
37
  fiber = Fiber.new(blocking: false, &block)
42
38
  fiber.resume
43
39
  end
@@ -75,8 +71,7 @@ module IO
75
71
  end
76
72
  end
77
73
 
78
- ready, @ready = @ready, []
79
- ready.each do |fiber|
74
+ while (fiber = @mutex.synchronize { @ready.shift })
80
75
  fiber.resume
81
76
  end
82
77
  end
@@ -118,19 +113,52 @@ module IO
118
113
  end
119
114
 
120
115
  def unblock(blocker, fiber)
121
- @ready << fiber
122
- io = @urgent.last
123
- io.write_nonblock('.')
116
+ @mutex.synchronize do
117
+ return false unless @urgent
118
+
119
+ @ready << fiber
120
+ @urgent.last.write_nonblock('.', exception: false)
121
+ end
124
122
  end
125
123
 
126
124
  def close
127
- run
128
- @urgent.each(&:close)
129
- @urgent = nil
125
+ first_error = nil
126
+
127
+ loop do
128
+ begin
129
+ run
130
+ break
131
+ rescue StandardError => e
132
+ first_error ||= e
133
+ break unless work_pending?
134
+ end
135
+ end
136
+
137
+ raise first_error if first_error
138
+ ensure
139
+ close_urgent_pipe
130
140
  end
131
141
 
132
142
  def closed?
133
- @urgent.nil?
143
+ @mutex.synchronize { @urgent.nil? }
144
+ end
145
+
146
+ def work_pending?
147
+ @mutex.synchronize do
148
+ @readable.any? || @writable.any? || @waiting.any? || @blocking.positive? || @ready.any? || @pending.any?
149
+ end
150
+ end
151
+
152
+ def reset!
153
+ @mutex.synchronize do
154
+ @readable.clear
155
+ @writable.clear
156
+ @waiting.clear
157
+ @ready.clear
158
+ @pending.clear
159
+ @blocking = 0
160
+ close_urgent_pipe_without_lock
161
+ end
134
162
  end
135
163
 
136
164
  def fiber(&block)
@@ -141,6 +169,15 @@ module IO
141
169
 
142
170
  private
143
171
 
172
+ def close_urgent_pipe
173
+ @mutex.synchronize { close_urgent_pipe_without_lock }
174
+ end
175
+
176
+ def close_urgent_pipe_without_lock
177
+ @urgent&.each { |io| io.close unless io.closed? }
178
+ @urgent = nil
179
+ end
180
+
144
181
  def current_time
145
182
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
146
183
  end
@@ -153,4 +190,4 @@ module IO
153
190
  end
154
191
 
155
192
  end
156
- end
193
+ end
@@ -11,6 +11,10 @@ class RelayManager
11
11
  def initialize
12
12
  @thread = nil
13
13
  @scheduler = FiberScheduler.new
14
+ @mutex = Mutex.new
15
+ @teardown_condition = ConditionVariable.new
16
+ @tearing_down = false
17
+ @running = false
14
18
  end
15
19
 
16
20
  # Add a IO relay to the manager. This will start relaying data from the source
@@ -26,11 +30,24 @@ class RelayManager
26
30
  # @param name [String] A human-friendly name for the relay used in debug output.
27
31
  # @param on_exit [#call] A callback to be invoked when sink can no longer be read from.
28
32
  def add_relay(sock, sink: nil, name: nil, on_exit: nil)
29
- @scheduler.schedule_fiber do
30
- relay_fiber(sock, sink, name, on_exit: on_exit)
33
+ @mutex.synchronize do
34
+ # An external caller that arrives during worker teardown waits until the
35
+ # scheduler is reusable. A callback on the worker itself must remain
36
+ # nonblocking; the scheduler's final run will process its accepted work.
37
+ while @tearing_down && Thread.current != @thread
38
+ @teardown_condition.wait(@mutex)
39
+ end
40
+
41
+ start_gate = start unless running?
42
+
43
+ begin
44
+ @scheduler.schedule_fiber do
45
+ relay_fiber(sock, sink, name, on_exit: on_exit)
46
+ end
47
+ ensure
48
+ start_gate << true if start_gate
49
+ end
31
50
  end
32
-
33
- start unless running?
34
51
  end
35
52
 
36
53
  # Write all data to the specified IO. This is intended to be used in scenarios
@@ -51,25 +68,66 @@ class RelayManager
51
68
  private
52
69
 
53
70
  def running?
54
- @thread && @thread.alive?
71
+ @running
55
72
  end
56
73
 
57
74
  def start
58
- return false if running?
75
+ return nil if running?
59
76
 
60
- @thread = Thread.new { run }
61
- true
77
+ start_gate = Queue.new
78
+ @running = true
79
+ @thread = Thread.new do
80
+ start_gate.pop
81
+ run
82
+ end
83
+ start_gate
84
+ rescue StandardError
85
+ @running = false
86
+ raise
62
87
  end
63
88
 
64
89
  def run
90
+ teardown_started = false
65
91
  old_scheduler = Fiber.scheduler
66
92
  # A fiber scheduler can be set per-thread
67
93
  Fiber.set_scheduler(@scheduler)
68
94
 
69
- # Run the scheduler (blocks here)
70
- @scheduler.run
95
+ loop do
96
+ # Run the scheduler (blocks here)
97
+ @scheduler.run
98
+
99
+ # #add_relay schedules and tests @running while holding this same lock.
100
+ # If it won the race after the scheduler observed an empty queue, process
101
+ # its accepted work before committing this worker to teardown.
102
+ teardown_started = @mutex.synchronize do
103
+ if @scheduler.work_pending?
104
+ false
105
+ else
106
+ @tearing_down = true
107
+ true
108
+ end
109
+ end
110
+ break if teardown_started
111
+ end
71
112
  ensure
72
- Fiber.set_scheduler(old_scheduler)
113
+ # On an exceptional exit, commit to teardown before removing the scheduler.
114
+ # External submissions then wait; callbacks on this worker remain able to
115
+ # schedule work for the scheduler's final run.
116
+ @mutex.synchronize { @tearing_down = true } unless teardown_started
117
+
118
+ begin
119
+ Fiber.set_scheduler(old_scheduler)
120
+ ensure
121
+ begin
122
+ @scheduler.reset!
123
+ ensure
124
+ @mutex.synchronize do
125
+ @running = false
126
+ @tearing_down = false
127
+ @teardown_condition.broadcast
128
+ end
129
+ end
130
+ end
73
131
  end
74
132
 
75
133
  def relay_fiber(sock, sink, name, on_exit: nil)
@@ -110,4 +168,4 @@ class RelayManager
110
168
  end
111
169
 
112
170
  end
113
- end
171
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rex-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.35
4
+ version: 0.1.37
5
5
  platform: ruby
6
6
  authors:
7
7
  - Metasploit Hackers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-12-02 00:00:00.000000000 Z
11
+ date: 2026-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake