summaryrefslogtreecommitdiffstats
path: root/chrome/browser/renderer_host/render_sandbox_host_linux.cc
blob: 8f265b1257059bb09be49ed2845ecd80a36aa5b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/renderer_host/render_sandbox_host_linux.h"

#include <fcntl.h>
#include <fontconfig/fontconfig.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <time.h>

#include <vector>

#include "base/command_line.h"
#include "base/eintr_wrapper.h"
#include "base/linux_util.h"
#include "base/pickle.h"
#include "base/process_util.h"
#include "base/scoped_ptr.h"
#include "base/shared_memory.h"
#include "base/singleton.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "chrome/common/font_config_ipc_linux.h"
#include "chrome/common/sandbox_methods_linux.h"
#include "chrome/common/unix_domain_socket_posix.h"
#include "skia/ext/SkFontHost_fontconfig_direct.h"
#include "third_party/npapi/bindings/npapi_extensions.h"
#include "third_party/WebKit/WebKit/chromium/public/gtk/WebFontInfo.h"

using WebKit::WebCString;
using WebKit::WebFontInfo;
using WebKit::WebUChar;

// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC

// BEWARE: code in this file run across *processes* (not just threads).

// This code runs in a child process
class SandboxIPCProcess  {
 public:
  // lifeline_fd: this is the read end of a pipe which the browser process
  //   holds the other end of. If the browser process dies, its descriptors are
  //   closed and we will noticed an EOF on the pipe. That's our signal to exit.
  // browser_socket: the browser's end of the sandbox IPC socketpair. From the
  //   point of view of the renderer, it's talking to the browser but this
  //   object actually services the requests.
  // sandbox_cmd: the path of the sandbox executable
  SandboxIPCProcess(int lifeline_fd, int browser_socket,
                    std::string sandbox_cmd)
      : lifeline_fd_(lifeline_fd),
        browser_socket_(browser_socket),
        font_config_(new FontConfigDirect()) {
    base::InjectiveMultimap multimap;
    multimap.push_back(base::InjectionArc(0, lifeline_fd, false));
    multimap.push_back(base::InjectionArc(0, browser_socket, false));

    base::CloseSuperfluousFds(multimap);

    if (!sandbox_cmd.empty()) {
      sandbox_cmd_.push_back(sandbox_cmd);
      sandbox_cmd_.push_back(base::kFindInodeSwitch);
    }
  }

  void Run() {
    struct pollfd pfds[2];
    pfds[0].fd = lifeline_fd_;
    pfds[0].events = POLLIN;
    pfds[1].fd = browser_socket_;
    pfds[1].events = POLLIN;

    int failed_polls = 0;
    for (;;) {
      const int r = HANDLE_EINTR(poll(pfds, 2, -1));
      if (r < 1) {
        LOG(WARNING) << "poll errno:" << errno;
        if (failed_polls++ == 3) {
          LOG(FATAL) << "poll failing. Sandbox host aborting.";
          return;
        }
        continue;
      }

      failed_polls = 0;

      if (pfds[0].revents) {
        // our parent died so we should too.
        _exit(0);
      }

      if (pfds[1].revents) {
        HandleRequestFromRenderer(browser_socket_);
      }
    }
  }

 private:
  // ---------------------------------------------------------------------------
  // Requests from the renderer...

  void HandleRequestFromRenderer(int fd) {
    std::vector<int> fds;

    // A FontConfigIPC::METHOD_MATCH message could be kMaxFontFamilyLength
    // bytes long (this is the largest message type).
    // 128 bytes padding are necessary so recvmsg() does not return MSG_TRUNC
    // error for a maximum length message.
    char buf[FontConfigInterface::kMaxFontFamilyLength + 128];

    const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds);
    if (len == -1) {
      // TODO: should send an error reply, or the sender might block forever.
      NOTREACHED()
          << "Sandbox host message is larger than kMaxFontFamilyLength";
      return;
    }
    if (fds.size() == 0)
      return;

    Pickle pickle(buf, len);
    void* iter = NULL;

    int kind;
    if (!pickle.ReadInt(&iter, &kind))
      goto error;

    if (kind == FontConfigIPC::METHOD_MATCH) {
      HandleFontMatchRequest(fd, pickle, iter, fds);
    } else if (kind == FontConfigIPC::METHOD_OPEN) {
      HandleFontOpenRequest(fd, pickle, iter, fds);
    } else if (kind == LinuxSandbox::METHOD_GET_FONT_FAMILY_FOR_CHARS) {
      HandleGetFontFamilyForChars(fd, pickle, iter, fds);
    } else if (kind == LinuxSandbox::METHOD_LOCALTIME) {
      HandleLocaltime(fd, pickle, iter, fds);
    } else if (kind == LinuxSandbox::METHOD_GET_CHILD_WITH_INODE) {
      HandleGetChildWithInode(fd, pickle, iter, fds);
    } else if (kind == LinuxSandbox::METHOD_GET_STYLE_FOR_STRIKE) {
      HandleGetStyleForStrike(fd, pickle, iter, fds);
    } else if (kind == LinuxSandbox::METHOD_MAKE_SHARED_MEMORY_SEGMENT) {
      HandleMakeSharedMemorySegment(fd, pickle, iter, fds);
    } else if (kind == LinuxSandbox::METHOD_MATCH_WITH_FALLBACK) {
      HandleMatchWithFallback(fd, pickle, iter, fds);
    }

  error:
    for (std::vector<int>::const_iterator
         i = fds.begin(); i != fds.end(); ++i) {
      close(*i);
    }
  }

  void HandleFontMatchRequest(int fd, const Pickle& pickle, void* iter,
                              std::vector<int>& fds) {
    bool filefaceid_valid;
    uint32_t filefaceid;

    if (!pickle.ReadBool(&iter, &filefaceid_valid))
      return;
    if (filefaceid_valid) {
      if (!pickle.ReadUInt32(&iter, &filefaceid))
        return;
    }
    bool is_bold, is_italic;
    if (!pickle.ReadBool(&iter, &is_bold) ||
        !pickle.ReadBool(&iter, &is_italic)) {
      return;
    }

    uint32_t characters_bytes;
    if (!pickle.ReadUInt32(&iter, &characters_bytes))
      return;
    const char* characters = NULL;
    if (characters_bytes > 0) {
      const uint32_t kMaxCharactersBytes = 1 << 10;
      if (characters_bytes % 2 != 0 ||  // We expect UTF-16.
          characters_bytes > kMaxCharactersBytes ||
          !pickle.ReadBytes(&iter, &characters, characters_bytes))
        return;
    }

    std::string family;
    if (!pickle.ReadString(&iter, &family))
      return;

    std::string result_family;
    unsigned result_filefaceid;
    const bool r = font_config_->Match(
        &result_family, &result_filefaceid, filefaceid_valid, filefaceid,
        family, characters, characters_bytes, &is_bold, &is_italic);

    Pickle reply;
    if (!r) {
      reply.WriteBool(false);
    } else {
      reply.WriteBool(true);
      reply.WriteUInt32(result_filefaceid);
      reply.WriteString(result_family);
      reply.WriteBool(is_bold);
      reply.WriteBool(is_italic);
    }
    SendRendererReply(fds, reply, -1);
  }

  void HandleFontOpenRequest(int fd, const Pickle& pickle, void* iter,
                             std::vector<int>& fds) {
    uint32_t filefaceid;
    if (!pickle.ReadUInt32(&iter, &filefaceid))
      return;
    const int result_fd = font_config_->Open(filefaceid);

    Pickle reply;
    if (result_fd == -1) {
      reply.WriteBool(false);
    } else {
      reply.WriteBool(true);
    }

    SendRendererReply(fds, reply, result_fd);

    if (result_fd >= 0)
      close(result_fd);
  }

  void HandleGetFontFamilyForChars(int fd, const Pickle& pickle, void* iter,
                                   std::vector<int>& fds) {
    // The other side of this call is
    // chrome/renderer/renderer_sandbox_support_linux.cc

    int num_chars;
    if (!pickle.ReadInt(&iter, &num_chars))
      return;

    // We don't want a corrupt renderer asking too much of us, it might
    // overflow later in the code.
    static const int kMaxChars = 4096;
    if (num_chars < 1 || num_chars > kMaxChars) {
      LOG(WARNING) << "HandleGetFontFamilyForChars: too many chars: "
                   << num_chars;
      return;
    }

    scoped_array<WebUChar> chars(new WebUChar[num_chars]);

    for (int i = 0; i < num_chars; ++i) {
      uint32_t c;
      if (!pickle.ReadUInt32(&iter, &c)) {
        return;
      }

      chars[i] = c;
    }

    WebCString family = WebFontInfo::familyForChars(chars.get(), num_chars);

    Pickle reply;
    if (family.data()) {
      reply.WriteString(family.data());
    } else {
      reply.WriteString("");
    }
    SendRendererReply(fds, reply, -1);
  }

  void HandleGetStyleForStrike(int fd, const Pickle& pickle, void* iter,
                               std::vector<int>& fds) {
    std::string family;
    int sizeAndStyle;

    if (!pickle.ReadString(&iter, &family) ||
        !pickle.ReadInt(&iter, &sizeAndStyle)) {
      return;
    }

    WebKit::WebFontRenderStyle style;
    WebFontInfo::renderStyleForStrike(family.c_str(), sizeAndStyle, &style);

    Pickle reply;
    reply.WriteInt(style.useBitmaps);
    reply.WriteInt(style.useAutoHint);
    reply.WriteInt(style.useHinting);
    reply.WriteInt(style.hintStyle);
    reply.WriteInt(style.useAntiAlias);
    reply.WriteInt(style.useSubpixel);

    SendRendererReply(fds, reply, -1);
  }

  void HandleLocaltime(int fd, const Pickle& pickle, void* iter,
                       std::vector<int>& fds) {
    // The other side of this call is in zygote_main_linux.cc

    std::string time_string;
    if (!pickle.ReadString(&iter, &time_string) ||
        time_string.size() != sizeof(time_t)) {
      return;
    }

    time_t time;
    memcpy(&time, time_string.data(), sizeof(time));
    // We use localtime here because we need the tm_zone field to be filled
    // out. Since we are a single-threaded process, this is safe.
    const struct tm* expanded_time = localtime(&time);

    std::string result_string;
    const char* time_zone_string = "";
    if (expanded_time != NULL) {
      result_string = std::string(reinterpret_cast<const char*>(expanded_time),
                                  sizeof(struct tm));
      time_zone_string = expanded_time->tm_zone;
    }

    Pickle reply;
    reply.WriteString(result_string);
    reply.WriteString(time_zone_string);
    SendRendererReply(fds, reply, -1);
  }

  void HandleGetChildWithInode(int fd, const Pickle& pickle, void* iter,
                               std::vector<int>& fds) {
    // The other side of this call is in zygote_main_linux.cc
    if (sandbox_cmd_.empty()) {
      LOG(ERROR) << "Not in the sandbox, this should not be called";
      return;
    }

    uint64_t inode;
    if (!pickle.ReadUInt64(&iter, &inode))
      return;

    base::ProcessId pid = 0;
    std::string inode_output;

    std::vector<std::string> sandbox_cmd = sandbox_cmd_;
    sandbox_cmd.push_back(base::Int64ToString(inode));
    CommandLine get_inode_cmd(sandbox_cmd);
    if (base::GetAppOutput(get_inode_cmd, &inode_output))
      base::StringToInt(inode_output, &pid);

    if (!pid) {
      // Even though the pid is invalid, we still need to reply to the zygote
      // and not just return here.
      LOG(ERROR) << "Could not get pid";
    }

    Pickle reply;
    reply.WriteInt(pid);
    SendRendererReply(fds, reply, -1);
  }

  void HandleMakeSharedMemorySegment(int fd, const Pickle& pickle, void* iter,
                                     std::vector<int>& fds) {
    uint32_t shm_size;
    if (!pickle.ReadUInt32(&iter, &shm_size))
      return;
    int shm_fd = -1;
    base::SharedMemory shm;
    if (shm.CreateAnonymous(shm_size))
      shm_fd = shm.handle().fd;
    Pickle reply;
    SendRendererReply(fds, reply, shm_fd);
  }

  void HandleMatchWithFallback(int fd, const Pickle& pickle, void* iter,
                               std::vector<int>& fds) {
    // Unlike the other calls, for which we are an indirection in front of
    // WebKit or Skia, this call is always made via this sandbox helper
    // process. Therefore the fontconfig code goes in here directly.

    std::string face;
    bool is_bold, is_italic;
    uint32 charset;

    if (!pickle.ReadString(&iter, &face) ||
        face.empty() ||
        !pickle.ReadBool(&iter, &is_bold) ||
        !pickle.ReadBool(&iter, &is_italic) ||
        !pickle.ReadUInt32(&iter, &charset)) {
      return;
    }

    FcLangSet* langset = FcLangSetCreate();
    MSCharSetToFontconfig(langset, charset);

    FcPattern* pattern = FcPatternCreate();
    // TODO(agl): FC_FAMILy needs to change
    FcPatternAddString(pattern, FC_FAMILY, (FcChar8*) face.c_str());
    if (is_bold)
      FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
    if (is_italic)
      FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
    FcPatternAddLangSet(pattern, FC_LANG, langset);
    FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
    FcConfigSubstitute(NULL, pattern, FcMatchPattern);
    FcDefaultSubstitute(pattern);

    FcResult result;
    FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
    int font_fd = -1;
    int good_enough_index = -1;
    bool good_enough_index_set = false;

    if (font_set) {
      for (int i = 0; i < font_set->nfont; ++i) {
        FcPattern* current = font_set->fonts[i];

        // Older versions of fontconfig have a bug where they cannot select
        // only scalable fonts so we have to manually filter the results.
        FcBool is_scalable;
        if (FcPatternGetBool(current, FC_SCALABLE, 0,
                             &is_scalable) != FcResultMatch ||
            !is_scalable) {
          continue;
        }

        FcChar8* c_filename;
        if (FcPatternGetString(current, FC_FILE, 0, &c_filename) !=
            FcResultMatch) {
          continue;
        }

        // We only want to return sfnt (TrueType) based fonts. We don't have a
        // very good way of detecting this so we'll filter based on the
        // filename.
        bool is_sfnt = false;
        static const char kSFNTExtensions[][5] = {
          ".ttf", ".otc", ".TTF", ".ttc", ""
        };
        const size_t filename_len = strlen(reinterpret_cast<char*>(c_filename));
        for (unsigned j = 0; ; j++) {
          if (kSFNTExtensions[j][0] == 0) {
            // None of the extensions matched.
            break;
          }
          const size_t ext_len = strlen(kSFNTExtensions[j]);
          if (filename_len > ext_len &&
              memcmp(c_filename + filename_len - ext_len,
                     kSFNTExtensions[j], ext_len) == 0) {
            is_sfnt = true;
            break;
          }
        }

        if (!is_sfnt)
          continue;

        // This font is good enough to pass muster, but we might be able to do
        // better with subsequent ones.
        if (!good_enough_index_set) {
          good_enough_index = i;
          good_enough_index_set = true;
        }

        FcValue matrix;
        bool have_matrix = FcPatternGet(current, FC_MATRIX, 0, &matrix) == 0;

        if (is_italic && have_matrix) {
          // we asked for an italic font, but fontconfig is giving us a
          // non-italic font with a transformation matrix.
          continue;
        }

        FcValue embolden;
        const bool have_embolden =
            FcPatternGet(current, FC_EMBOLDEN, 0, &embolden) == 0;

        if (is_bold && have_embolden) {
          // we asked for a bold font, but fontconfig gave us a non-bold font
          // and asked us to apply fake bolding.
          continue;
        }

        font_fd = open(reinterpret_cast<char*>(c_filename), O_RDONLY);
        if (font_fd >= 0)
          break;
      }
    }

    if (font_fd == -1 && good_enough_index_set) {
      // We didn't find a font that we liked, so we fallback to something
      // acceptable.
      FcPattern* current = font_set->fonts[good_enough_index];
      FcChar8* c_filename;
      FcPatternGetString(current, FC_FILE, 0, &c_filename);
      font_fd = open(reinterpret_cast<char*>(c_filename), O_RDONLY);
    }

    if (font_set)
      FcFontSetDestroy(font_set);
    FcPatternDestroy(pattern);

    Pickle reply;
    SendRendererReply(fds, reply, font_fd);

    if (font_fd >= 0) {
      if (HANDLE_EINTR(close(font_fd)) < 0)
        PLOG(ERROR) << "close";
    }
  }

  // MSCharSetToFontconfig translates a Microsoft charset identifier to a
  // fontconfig language set by appending to |langset|.
  static void MSCharSetToFontconfig(FcLangSet* langset, unsigned fdwCharSet) {
    // We have need to translate raw fdwCharSet values into terms that
    // fontconfig can understand. (See the description of fdwCharSet in the MSDN
    // documentation for CreateFont:
    // http://msdn.microsoft.com/en-us/library/dd183499(VS.85).aspx )
    //
    // Although the argument is /called/ 'charset', the actual values conflate
    // character sets (which are sets of Unicode code points) and character
    // encodings (which are algorithms for turning a series of bits into a
    // series of code points.) Sometimes the values will name a language,
    // sometimes they'll name an encoding. In the latter case I'm assuming that
    // they mean the set of code points in the domain of that encoding.
    //
    // fontconfig deals with ISO 639-1 language codes:
    //   http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
    //
    // So, for each of the documented fdwCharSet values I've had to take a
    // guess at the set of ISO 639-1 languages intended.

    switch (fdwCharSet) {
      case NPCharsetAnsi:
      // These values I don't really know what to do with, so I'm going to map
      // them to English also.
      case NPCharsetDefault:
      case NPCharsetMac:
      case NPCharsetOEM:
      case NPCharsetSymbol:
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("en"));
        break;
      case NPCharsetBaltic:
        // The three baltic languages.
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("et"));
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lv"));
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lt"));
        break;
      // TODO(jungshik): Would we be better off mapping Big5 to zh-tw
      // and GB2312 to zh-cn? Fontconfig has 4 separate orthography
      // files (zh-{cn,tw,hk,mo}.
      case NPCharsetChineseBIG5:
      case NPCharsetGB2312:
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh"));
        break;
      case NPCharsetEastEurope:
        // A scattering of eastern European languages.
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("pl"));
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("cs"));
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("sk"));
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hu"));
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hr"));
        break;
      case NPCharsetGreek:
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("el"));
        break;
      case NPCharsetHangul:
      case NPCharsetJohab:
        // Korean
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ko"));
        break;
      case NPCharsetRussian:
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ru"));
        break;
      case NPCharsetShiftJIS:
        // Japanese
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ja"));
        break;
      case NPCharsetTurkish:
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("tr"));
        break;
      case NPCharsetVietnamese:
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("vi"));
        break;
      case NPCharsetArabic:
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ar"));
        break;
      case NPCharsetHebrew:
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("he"));
        break;
      case NPCharsetThai:
        FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("th"));
        break;
      // default:
      // Don't add any languages in that case that we don't recognise the
      // constant.
    }
  }

  void SendRendererReply(const std::vector<int>& fds, const Pickle& reply,
                         int reply_fd) {
    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));
    struct iovec iov = {const_cast<void*>(reply.data()), reply.size()};
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    char control_buffer[CMSG_SPACE(sizeof(int))];

    if (reply_fd != -1) {
      struct stat st;
      if (fstat(reply_fd, &st) == 0 && S_ISDIR(st.st_mode)) {
        LOG(FATAL) << "Tried to send a directory descriptor over sandbox IPC";
        // We must never send directory descriptors to a sandboxed process
        // because they can use openat with ".." elements in the path in order
        // to escape the sandbox and reach the real filesystem.
      }

      struct cmsghdr *cmsg;
      msg.msg_control = control_buffer;
      msg.msg_controllen = sizeof(control_buffer);
      cmsg = CMSG_FIRSTHDR(&msg);
      cmsg->cmsg_level = SOL_SOCKET;
      cmsg->cmsg_type = SCM_RIGHTS;
      cmsg->cmsg_len = CMSG_LEN(sizeof(int));
      memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(reply_fd));
      msg.msg_controllen = cmsg->cmsg_len;
    }

    if (HANDLE_EINTR(sendmsg(fds[0], &msg, MSG_DONTWAIT)) < 0)
      PLOG(ERROR) << "sendmsg";
  }

  // ---------------------------------------------------------------------------

  const int lifeline_fd_;
  const int browser_socket_;
  FontConfigDirect* const font_config_;
  std::vector<std::string> sandbox_cmd_;
};

// -----------------------------------------------------------------------------

// Runs on the main thread at startup.
RenderSandboxHostLinux::RenderSandboxHostLinux()
    : initialized_(false),
      renderer_socket_(0),
      childs_lifeline_fd_(0),
      pid_(0) {
}

// static
RenderSandboxHostLinux* RenderSandboxHostLinux::GetInstance() {
  return Singleton<RenderSandboxHostLinux>::get();
}

void RenderSandboxHostLinux::Init(const std::string& sandbox_path) {
  DCHECK(!initialized_);
  initialized_ = true;

  int fds[2];
  // We use SOCK_SEQPACKET rather than SOCK_DGRAM to prevent the renderer from
  // sending datagrams to other sockets on the system. The sandbox may prevent
  // the renderer from calling socket() to create new sockets, but it'll still
  // inherit some sockets. With PF_UNIX+SOCK_DGRAM, it can call sendmsg to send
  // a datagram to any (abstract) socket on the same system. With
  // SOCK_SEQPACKET, this is prevented.
  CHECK(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);

  renderer_socket_ = fds[0];
  const int browser_socket = fds[1];

  int pipefds[2];
  CHECK(0 == pipe(pipefds));
  const int child_lifeline_fd = pipefds[0];
  childs_lifeline_fd_ = pipefds[1];

  pid_ = fork();
  if (pid_ == 0) {
    SandboxIPCProcess handler(child_lifeline_fd, browser_socket, sandbox_path);
    handler.Run();
    _exit(0);
  }
}

RenderSandboxHostLinux::~RenderSandboxHostLinux() {
  if (initialized_) {
    if (HANDLE_EINTR(close(renderer_socket_)) < 0)
      PLOG(ERROR) << "close";
    if (HANDLE_EINTR(close(childs_lifeline_fd_)) < 0)
      PLOG(ERROR) << "close";
  }
}