Mercurial > public > mercurial-scm > hg
comparison mercurial/commandserver.py @ 23324:69f86b937035
cmdserver: protect pipe server streams against corruption caused by direct io
Because pipe-mode server uses stdio as IPC channel, other modules should not
touch stdio directly and use ui instead. However, this strategy is brittle
because several Python functions read and write stdio implicitly.
print 'hello' # should use ui.write()
# => ch = 'h', size = 1701604463 'ello', data = '\n'
This patch adds protection for such mistakes. Both stdio files and low-level
file descriptors are redirected to /dev/null while command server uses them.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Sat, 15 Nov 2014 13:50:43 +0900 |
parents | bc374458688b |
children | 328739ea70c3 |
comparison
equal
deleted
inserted
replaced
23323:bc374458688b | 23324:69f86b937035 |
---|---|
5 # This software may be used and distributed according to the terms of the | 5 # This software may be used and distributed according to the terms of the |
6 # GNU General Public License version 2 or any later version. | 6 # GNU General Public License version 2 or any later version. |
7 | 7 |
8 from i18n import _ | 8 from i18n import _ |
9 import struct | 9 import struct |
10 import os, errno, traceback, SocketServer | 10 import sys, os, errno, traceback, SocketServer |
11 import dispatch, encoding, util | 11 import dispatch, encoding, util |
12 | 12 |
13 logfile = None | 13 logfile = None |
14 | 14 |
15 def log(*args): | 15 def log(*args): |
246 # its request | 246 # its request |
247 return 1 | 247 return 1 |
248 | 248 |
249 return 0 | 249 return 0 |
250 | 250 |
251 def _protectio(ui): | |
252 """ duplicates streams and redirect original to null if ui uses stdio """ | |
253 ui.flush() | |
254 newfiles = [] | |
255 nullfd = os.open(os.devnull, os.O_RDWR) | |
256 for f, sysf, mode in [(ui.fin, sys.stdin, 'rb'), | |
257 (ui.fout, sys.stdout, 'wb')]: | |
258 if f is sysf: | |
259 newfd = os.dup(f.fileno()) | |
260 os.dup2(nullfd, f.fileno()) | |
261 f = os.fdopen(newfd, mode) | |
262 newfiles.append(f) | |
263 os.close(nullfd) | |
264 return tuple(newfiles) | |
265 | |
266 def _restoreio(ui, fin, fout): | |
267 """ restores streams from duplicated ones """ | |
268 ui.flush() | |
269 for f, uif in [(fin, ui.fin), (fout, ui.fout)]: | |
270 if f is not uif: | |
271 os.dup2(f.fileno(), uif.fileno()) | |
272 f.close() | |
273 | |
251 class pipeservice(object): | 274 class pipeservice(object): |
252 def __init__(self, ui, repo, opts): | 275 def __init__(self, ui, repo, opts): |
253 self.ui = ui | 276 self.ui = ui |
254 self.repo = repo | 277 self.repo = repo |
255 | 278 |
256 def init(self): | 279 def init(self): |
257 pass | 280 pass |
258 | 281 |
259 def run(self): | 282 def run(self): |
260 ui = self.ui | 283 ui = self.ui |
261 sv = server(ui, self.repo, ui.fin, ui.fout) | 284 # redirect stdio to null device so that broken extensions or in-process |
262 return sv.serve() | 285 # hooks will never cause corruption of channel protocol. |
286 fin, fout = _protectio(ui) | |
287 try: | |
288 sv = server(ui, self.repo, fin, fout) | |
289 return sv.serve() | |
290 finally: | |
291 _restoreio(ui, fin, fout) | |
263 | 292 |
264 class _requesthandler(SocketServer.StreamRequestHandler): | 293 class _requesthandler(SocketServer.StreamRequestHandler): |
265 def handle(self): | 294 def handle(self): |
266 ui = self.server.ui | 295 ui = self.server.ui |
267 repo = self.server.repo | 296 repo = self.server.repo |